TL;DR: Descriptor in Python is just getter/setter
Three ways to implement descriptor:
- Use a class that implement magic method
__get__
,__set__
,__delete__
- Use builtin function
property(fget=None, fset=None, fdel=None, doc=None)
- Use decorator form of
property
, like@property def attr(): ...
,@attr.setter
,@attr.deleter
Make sure all three methods are done at class level rather than at instance level.
(That is, write my_attr = property(...)
under class MyClass(object):
statement,
but not self.my_attr = property(..)
in __init__
)
Because Python’s MRO is:
- find
attr
ininstance.__dict__
(instance level) - if not found, find
attr
ininstance.__class__.__dict__
. If found, try returnattr.__get__
, otherwise returnattr
(class level) - if not found, repeat step 2 on
instance.__class__.__base__
untilattr
found or no base class found - if not found, try returning computed
attr
if__getattr__
is defined
So the descriptor magic is done at class level
Pitfall:
Because descriptor dwells at class level, if one uses descriptor class implementation, all instances may share one common variable.
To fix that:
- Hoard one hidden variable in the instance
- Use a dictionary in descriptor class to store info of different instances.
The second solution needs the class hashable. (So a meta class is needed to guarantee hashability)
Personal view: To maintain Python’s one simple way to work
Pythonists just introduced much more complexity
.
Reference:
http://www.ibm.com/developerworks/library/os-pythondescriptors/
http://nbviewer.ipython.org/urls/gist.github.com/ChrisBeaumont/5758381/raw/descriptor_writeup.ipynb
http://docs.python.org/2/howto/descriptor.html