How do Python properties work?

PythonProperties

Python Problem Overview


I've been successfully using Python properties, but I don't see how they could work. If I dereference a property outside of a class, I just get an object of type property:

@property
def hello(): return "Hello, world!"

hello  # <property object at 0x9870a8>

But if I put a property in a class, the behavior is very different:

class Foo(object):
   @property
   def hello(self): return "Hello, world!"

Foo().hello # 'Hello, world!'

I've noticed that unbound Foo.hello is still the property object, so class instantiation must be doing the magic, but what magic is that?

Python Solutions


Solution 1 - Python

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.

Solution 2 - Python

In order for @properties to work properly the class needs to be a subclass of object. when the class is not a subclass of object then the first time you try access the setter it actually makes a new attribute with the shorter name instead of accessing through the setter.

The following does not work correctly.

class C(): # <-- Notice that object is missing

    def __init__(self):
        self._x = None
    
    @property
    def x(self):
        print 'getting value of x'
        return self._x
    
    @x.setter
    def x(self, x):
        print 'setting value of x'
        self._x = x
    
>>> c = C()
>>> c.x = 1
>>> print c.x, c._x
1 0

The following will work correctly

class C(object):

    def __init__(self):
        self._x = None
        
    @property
    def x(self):
        print 'getting value of x'
        return self._x
        
    @x.setter
    def x(self, x):
        print 'setting value of x'
        self._x = x
        
>>> c = C()
>>> c.x = 1
setting value of x
>>> print c.x, c._x
getting value of x
1 1

Solution 3 - Python

Properties are descriptors, and descriptors behave specially when member of a class instance. In short, if a is an instance of type A, and A.foo is a descriptor, then a.foo is equivalent to A.foo.__get__(a).

Solution 4 - Python

The property object just implements the descriptor protocol: http://docs.python.org/howto/descriptor.html

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFred FooView Question on Stackoverflow
Solution 1 - PythonTim YatesView Answer on Stackoverflow
Solution 2 - PythonTimothy VannView Answer on Stackoverflow
Solution 3 - PythonSven MarnachView Answer on Stackoverflow
Solution 4 - PythonAchimView Answer on Stackoverflow