Real World Example About How to Use Property Feature in Python

When is a property needed instead of just exposing the variable in Python?

What I think personally is that Python exposes variables as part of its philosophy, so you do not need to make properties out of everything. You can use properties to add logic to what was once a simple variable, or in order to keep the (client) code clean.

Classes like C in are useless outside tutorials/documentation.

(This is my own opinion. I don't know what is "considered" bad practice, as I am not an experienced developer or something)

How does the @property decorator work in Python?

The property() function returns a special descriptor object:

>>> property()
<property object at 0x10ff07940>

It is this object that has extra methods:

>>> property().getter
<built-in method getter of property object at 0x10ff07998>
>>> property().setter
<built-in method setter of property object at 0x10ff07940>
>>> property().deleter
<built-in method deleter of property object at 0x10ff07998>

These act as decorators too. They return a new property object:

>>> property().getter(None)
<property object at 0x10ff079f0>

that is a copy of the old object, but with one of the functions replaced.

Remember, that the @decorator syntax is just syntactic sugar; the syntax:

@property
def foo(self): return self._foo

really means the same thing as

def foo(self): return self._foo
foo = property(foo)

so foo the function is replaced by property(foo), which we saw above is a special object. Then when you use @foo.setter(), what you are doing is call that property().setter method I showed you above, which returns a new copy of the property, but this time with the setter function replaced with the decorated method.

The following sequence also creates a full-on property, by using those decorator methods.

First we create some functions and a property object with just a getter:

>>> def getter(self): print('Get!')
...
>>> def setter(self, value): print('Set to {!r}!'.format(value))
...
>>> def deleter(self): print('Delete!')
...
>>> prop = property(getter)
>>> prop.fget is getter
True
>>> prop.fset is None
True
>>> prop.fdel is None
True

Next we use the .setter() method to add a setter:

>>> prop = prop.setter(setter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is None
True

Last we add a deleter with the .deleter() method:

>>> prop = prop.deleter(deleter)
>>> prop.fget is getter
True
>>> prop.fset is setter
True
>>> prop.fdel is deleter
True

Last but not least, the property object acts as a descriptor object, so it has .__get__(), .__set__() and .__delete__() methods to hook into instance attribute getting, setting and deleting:

>>> class Foo: pass
...
>>> prop.__get__(Foo(), Foo)
Get!
>>> prop.__set__(Foo(), 'bar')
Set to 'bar'!
>>> prop.__delete__(Foo())
Delete!

The Descriptor Howto includes a pure Python sample implementation of the property() type:

class Property:
"Emulate PyProperty_Type() in Objects/descrobject.c"

def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
if doc is None and fget is not None:
doc = fget.__doc__
self.__doc__ = doc

def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)

def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj, value)

def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj)

def getter(self, fget):
return type(self)(fget, self.fset, self.fdel, self.__doc__)

def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.__doc__)

def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel, self.__doc__)

python property decorator

It's simply syntactic sugar. It allows a method call to look like a variable access or assignment.

One way this can be useful is if you want to change something that previously was a simple variable to something that's actually computed or validated with other code. If you make it a property, you can do this without breaking any existing code. Another way is for caching, lazy initialization, etc., of object attributes.

Usefulness of property decorator in python for mutable attributes

You created an attribute that can't be deleted:

>>> f = Foo2('bar')
>>> f.prop
'bar'
>>> f.prop = 'spam'
>>> f.prop
'spam'
>>> del f.prop
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't delete attribute

Compare this to the Foo().prop attribute, which can be deleted:

>>> f = Foo('bar')
>>> f.prop
'bar'
>>> del f.prop
>>> f.prop
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Foo' object has no attribute 'prop'

Apart from that, or if you added a @prop.deleter handler, there is not really any advantage to creating such a property. Delegating to a function what normal attribute access can do is not that useful.

Property setters or getters are much more useful when they do something in addition to just setting the attribute, like validation or transformation.

For example, the setter could enforce that values are always integers, including setting a default value if conversion to an integer fails:

@prop.setter
def prop(self, value):
try:
self._prop = int(prop)
except ValueError:
self._prop = 0

In other programming languages such as Java, you can't easily convert attributes to getters and setters (there is no equivalent concept to Python properties in Java) without having to re-write all access to those attributes everywhere in your code-base, so in those languages you often see advice to start out with getters and setters. This doesn't apply to Python, you can trivially turn existing attributes into properties without having to change any code using those attributes.

Python - multiple @property statements in class definition?

No, you can use multiple @property decorators to your heart's content. There is no limit here, other than that of example writers imaginations, apparently.

The Python standard library is full of @property use if you want examples:

  • numbers defines ABCs for the numbers classes in Python.

  • tempfile implements temporary file objects

  • threading provding higher-level thread support

  • urlparse for handling URLs and query strings.

etc.

You had it spot on; multiple properties would look exactly like what you posted.

Using @property versus getters and setters

Prefer properties. It's what they're there for.

The reason is that all attributes are public in Python. Starting names with an underscore or two is just a warning that the given attribute is an implementation detail that may not stay the same in future versions of the code. It doesn't prevent you from actually getting or setting that attribute. Therefore, standard attribute access is the normal, Pythonic way of, well, accessing attributes.

The advantage of properties is that they are syntactically identical to attribute access, so you can change from one to another without any changes to client code. You could even have one version of a class that uses properties (say, for code-by-contract or debugging) and one that doesn't for production, without changing the code that uses it. At the same time, you don't have to write getters and setters for everything just in case you might need to better control access later.

Real-world examples of nested functions

Your question made me curious, so I looked in some real-world code: the Python standard library. I found 67 examples of nested functions. Here are a few, with explanations.

One very simple reason to use a nested function is simply that the function you're defining doesn't need to be global, because only the enclosing function uses it. A typical example from Python's quopri.py standard library module:

def encode(input, output, quotetabs, header = 0):
...
def write(s, output=output, lineEnd='\n'):
# RFC 1521 requires that the line ending in a space or tab must have
# that trailing character encoded.
if s and s[-1:] in ' \t':
output.write(s[:-1] + quote(s[-1]) + lineEnd)
elif s == '.':
output.write(quote(s) + lineEnd)
else:
output.write(s + lineEnd)

... # 35 more lines of code that call write in several places

Here there was some common code within the encode function, so the author simply factored it out into a write function.


Another common use for nested functions is re.sub. Here's some code from the json/encode.py standard library module:

def encode_basestring(s):
"""Return a JSON representation of a Python string

"""
def replace(match):
return ESCAPE_DCT[match.group(0)]
return '"' + ESCAPE.sub(replace, s) + '"'

Here ESCAPE is a regular expression, and ESCAPE.sub(replace, s) finds all matches of ESCAPE in s and replaces each one with replace(match).


In fact, any API, like re.sub, that accepts a function as a parameter can lead to situations where nested functions are convenient. For example, in turtle.py there's some silly demo code that does this:

    def baba(xdummy, ydummy):
clearscreen()
bye()

...
tri.write(" Click me!", font = ("Courier", 12, "bold") )
tri.onclick(baba, 1)

onclick expects you to pass an event-handler function, so we define one and pass it in.

Is there a built-in function to print all the current properties and values of an object?

You are really mixing together two different things.

Use dir(), vars() or the inspect module to get what you are interested in (I use __builtins__ as an example; you can use any object instead).

>>> l = dir(__builtins__)
>>> d = __builtins__.__dict__

Print that dictionary however fancy you like:

>>> print l
['ArithmeticError', 'AssertionError', 'AttributeError',...

or

>>> from pprint import pprint
>>> pprint(l)
['ArithmeticError',
'AssertionError',
'AttributeError',
'BaseException',
'DeprecationWarning',
...

>>> pprint(d, indent=2)
{ 'ArithmeticError': <type 'exceptions.ArithmeticError'>,
'AssertionError': <type 'exceptions.AssertionError'>,
'AttributeError': <type 'exceptions.AttributeError'>,
...
'_': [ 'ArithmeticError',
'AssertionError',
'AttributeError',
'BaseException',
'DeprecationWarning',
...

Pretty printing is also available in the interactive debugger as a command:

(Pdb) pp vars()
{'__builtins__': {'ArithmeticError': <type 'exceptions.ArithmeticError'>,
'AssertionError': <type 'exceptions.AssertionError'>,
'AttributeError': <type 'exceptions.AttributeError'>,
'BaseException': <type 'exceptions.BaseException'>,
'BufferError': <type 'exceptions.BufferError'>,
...
'zip': <built-in function zip>},
'__file__': 'pass.py',
'__name__': '__main__'}


Related Topics



Leave a reply



Submit