How Do Python Properties Work

How do Python properties work?

As others have noted, they use a language feature called descriptors.

The reason that the actual property object is returned when you access it via a class Foo.hello lies in how the property implements the __get__(self, instance, owner) special method:

  • If a descriptor is accessed on an instance, then that instance is passed as the appropriate argument, and owner is the class of that instance.
  • When it is accessed through the class, then instance is None and only owner is passed. The property object recognizes this and returns self.

Besides the Descriptors howto, see also the documentation on Implementing Descriptors and Invoking Descriptors in the Language Guide.

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__)

How does property decorator work internally using syntactic sugar(@) in python?

The difference is exactly where you asked yourself if it was (exactly?) the same.

PEP 318 mentions it rapidly:

Current Syntax

The current syntax for function decorators as
implemented in Python 2.4a2 is:

@dec2
@dec1
def func(arg1, arg2, ...):
pass

This is equivalent to:

def func(arg1, arg2, ...):
pass
func = dec2(dec1(func))

without the intermediate assignment to the variable func. (emphasis mine)

So, that's why everything works fine when you use the @decorator syntax.

You could make it work by using a different name:

class C:
def __init__(self, language):
self._language = language


def language(self):
return self._language
language = property(language)


def dummy_temporary_name(self, value):
self._language = value
language = language.setter(dummy_temporary_name)


c = C('fr')
print(c.language)
c.language = 'de'
print(c.language)

# fr
# de

When and how to use the builtin function property() in python

In languages that rely on getters and setters, like Java, they're not supposed nor expected to do anything but what they say -- it would be astonishing if x.getB() did anything but return the current value of logical attribute b, or if x.setB(2) did anything but whatever small amount of internal work is needed to make x.getB() return 2.

However, there are no language-imposed guarantees about this expected behavior, i.e., compiler-enforced constraints on the body of methods whose names start with get or set: rather, it's left up to common sense, social convention, "style guides", and testing.

The behavior of x.b accesses, and assignments such as x.b = 2, in languages which do have properties (a set of languages which includes but is not limited to Python) is exactly the same as for getter and setter methods in, e.g., Java: the same expectations, the same lack of language-enforced guarantees.

The first win for properties is syntax and readability. Having to write, e.g.,

x.setB(x.getB() + 1)

instead of the obvious

x.b += 1

cries out for vengeance to the gods. In languages which support properties, there is absolutely no good reason to force users of the class to go through the gyrations of such Byzantine boilerplate, impacting their code's readability with no upside whatsoever.

In Python specifically, there's one more great upside to using properties (or other descriptors) in lieu of getters and setters: if and when you reorganize your class so that the underlying setter and getter are not needed anymore, you can (without breaking the class's published API) simply eliminate those methods and the property that relies on them, making b a normal "stored" attribute of x's class rather than a "logical" one obtained and set computationally.

In Python, doing things directly (when feasible) instead of via methods is an important optimization, and systematically using properties enables you to perform this optimization whenever feasible (always exposing "normal stored attributes" directly, and only ones which do need computation upon access and/or setting via methods and properties).

So, if you use getters and setters instead of properties, beyond impacting the readability of your users' code, you are also gratuitously wasting machine cycles (and the energy that goes to their computer during those cycles;-), again for no good reason whatsoever.

Your only argument against properties is e.g. that "an outside user wouldn't expect any side effects as a result of an assignment, usually"; but you miss the fact that the same user (in a language such as Java where getters and setters are pervasive) wouldn't expect (observable) "side effects" as a result of calling a setter, either (and even less for a getter;-). They're reasonable expectations and it's up to you, as the class author, to try and accommodate them -- whether your setter and getter are used directly or through a property, makes no difference. If you have methods with important observable side effects, do not name them getThis, setThat, and do not use them via properties.

The complaint that properties "hide the implementation" is wholly unjustified: most all of OOP is about implementing information hiding -- making a class responsible for presenting a logical interface to the outside world and implementing it internally as best it can. Getters and setters, exactly like properties, are tools towards this goal. Properties just do a better job at it (in languages that support them;-).

accessing value of a python @property from a subclass

property objects are descriptors, which means they are automatically bound to an instance when accessed as an attribute (this is also how methods are created).

If you wanted to access a parent property object, you could just bind it manually by calling the descriptor.__get__() method and passing in self:

Parent.fieldname.__get__(self))

The property.fget() method is simply the original, un-decorated function object. You'd call it like any unbound method, you'd pass in self manually again:

Parent.fieldname.fget(self)

or bind it like a method, then call:

Parent.fieldname.fget.__get__(self)()

Last, but not least, you could use the super() object to take care of the binding for you:

super(Child, self).fieldname

which finds the next object in the MRO (method resolution order) of the class hierarchy that has a fieldname attribute and binds it just like the explicit example above.

Personally, I prefer the super() option; it best documents that you want to access the original, now overridden property.

Demo:

>>> class Parent(object):
... @property
... def fieldname(self):
... """returns a tuple"""
... return (1, 10)
...
>>> class Child(Parent):
... @property
... def fieldname(self):
... """returns a tuple"""
... return self._config.get(
... 'fieldname',
... super(Child, self).fieldname)
...
>>> child = Child()
>>> child._config = {}
>>> child.fieldname
(1, 10)
>>> child._config['fieldname'] = ('foo', 'bar')
>>> child.fieldname
('foo', 'bar')

Python get and set methods versus @property decorator

The best part of using property for an attribute is that you don't need it.

The philosophy in Python is that classes attributes and methods are all public, but by convention - when you prefix their name with a single "_"

The mechanism behing "property", the descriptor protocol, allows one to change a previous dumb plain attribute into an instrumented attribute, guarded with code for the getter and setter, if the system evolves to a situation where it is needed.

But by default, a name attribute in a class, is just a plain attribute. You do person.name = "Name"- no guards needed, no setting method needed nor recommended. When and if it one needs a guard for that (say, to capitalize the name, or filter on improper words), whatever code uses that attribute needs no change: with the use of property, attribute assignment still takes place with the "=" operator.

Other than that, if using "=" does not look prettier than person.set_name("Name") for you, I think it does for most people. Of course, that is subjective.



Related Topics



Leave a reply



Submit