How do Python properties work?
I've been successfully using Python propertie开发者_运维知识库s, 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?
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 onlyowner
is passed. Theproperty
object recognizes this and returnsself
.
Besides the Descriptors howto, see also the documentation on Implementing Descriptors and Invoking Descriptors in the Language Guide.
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
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)
.
The property
object just implements the descriptor protocol: http://docs.python.org/howto/descriptor.html
精彩评论