Inconsistent behavior of descriptors
Bengt Richter
bokr at oz.net
Wed Oct 1 08:26:54 EDT 2003
On Tue, 30 Sep 2003 20:15:10 +0400 (MSD), "Denis S. Otkidach" <ods at strana.ru> wrote:
>
>I've noticed that the order of attribute lookup is inconsistent
>when descriptor is used. property instance takes precedence of
>instance attributes:
>
>>>> class A(object):
>... def _get_attr(self):
>... return self._attr
>... attr = property(_get_attr)
>...
>>>> a=A()
>>>> a.__dict__
>{}
>>>> a.__dict__['attr']=1
>>>> a.__dict__
>{'attr': 1}
>>>> a.attr
>Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> File "<stdin>", line 3, in _get_attr
>AttributeError: 'A' object has no attribute '_attr'
>
>But it doesn't when I use custom class of descriptor:
>
>>>> class descr(object):
>... def __get__(self, inst, cls):
>... return inst._attr
>...
>>>> class B(object):
>... attr = descr()
>...
>>>> b=B()
>>>> b.__dict__
>{}
>>>> b.__dict__['attr']=1
>>>> b.__dict__
>{'attr': 1}
>>>> b.attr
>1
>
>Subclasses of property behave like property itself:
>
>>>> class descr2(property):
>... def __get__(self, inst, cls):
>... return inst._attr
>...
>>>> class C(object):
>... attr = descr2()
>...
>>>> c=C()
>>>> c.__dict__
>{}
>>>> c.__dict__['attr']=1
>>>> c.__dict__
>{'attr': 1}
>>>> c.attr
>Traceback (most recent call last):
> File "<stdin>", line 1, in ?
> File "<stdin>", line 3, in __get__
>AttributeError: 'C' object has no attribute '_attr'
>
>Is it an undocumented feature or I have to submit a bug report?
>
I think (not having read the above carefully) that it's all documented in
http://users.rcn.com/python/download/Descriptor.htm
(thanks to Raymond Hettinger).
excerpt:
"""
The details of invocation depend on whether obj is an object or a class.
Either way, descriptors only work for new style objects and classes. A
class is new style if it is a subclass of object.
For objects, the machinery is in object.__getattribute__ which
transforms b.x into type(b).__dict__['x'].__get__(b, type(b)). The
implementation works through a precedence chain that gives data
descriptors priority over instance variables, instance variables
priority over non-data descriptors, and assigns lowest priority to
__getattr__ if provided. The full C implementation can be found in
PyObject_GenericGetAttr() in Objects/object.c.
For classes, the machinery is in type.__getattribute__ which transforms
B.x into B.__dict__['x'].__get__(None, B). In pure Python, it looks
like:
def __getattribute__(self, key):
"Emulate type_getattro() in Objects/typeobject.c"
v = object.__getattribute__(self, key)
if hasattr(v, '__get__'):
return v.__get__(None, self)
return v
The important points to remember are:
descriptors are invoked by the __getattribute__ method
overriding __getattribute__ prevents automatic descriptor calls
__getattribute__ is only available with new style classes and objects
object.__getattribute__ and type.__getattribute__ make different calls to __get__.
data descriptors always override instance dictionaries.
non-data descriptors may be overridden by instance dictionaries.
"""
Regards,
Bengt Richter
More information about the Python-list
mailing list