Inconsistent behavior of descriptors

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Denis S. Otkidach

    #1

    Inconsistent behavior of descriptors


    I've noticed that the order of attribute lookup is inconsistent
    when descriptor is used. property instance takes precedence of
    instance attributes:
    [color=blue][color=green][color=darkred]
    >>> class A(object):[/color][/color][/color]
    .... def _get_attr(self) :
    .... return self._attr
    .... attr = property(_get_a ttr)
    ....[color=blue][color=green][color=darkred]
    >>> a=A()
    >>> a.__dict__[/color][/color][/color]
    {}[color=blue][color=green][color=darkred]
    >>> a.__dict__['attr']=1
    >>> a.__dict__[/color][/color][/color]
    {'attr': 1}[color=blue][color=green][color=darkred]
    >>> a.attr[/color][/color][/color]
    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:
    [color=blue][color=green][color=darkred]
    >>> class descr(object):[/color][/color][/color]
    .... def __get__(self, inst, cls):
    .... return inst._attr
    ....[color=blue][color=green][color=darkred]
    >>> class B(object):[/color][/color][/color]
    .... attr = descr()
    ....[color=blue][color=green][color=darkred]
    >>> b=B()
    >>> b.__dict__[/color][/color][/color]
    {}[color=blue][color=green][color=darkred]
    >>> b.__dict__['attr']=1
    >>> b.__dict__[/color][/color][/color]
    {'attr': 1}[color=blue][color=green][color=darkred]
    >>> b.attr[/color][/color][/color]
    1

    Subclasses of property behave like property itself:
    [color=blue][color=green][color=darkred]
    >>> class descr2(property ):[/color][/color][/color]
    .... def __get__(self, inst, cls):
    .... return inst._attr
    ....[color=blue][color=green][color=darkred]
    >>> class C(object):[/color][/color][/color]
    .... attr = descr2()
    ....[color=blue][color=green][color=darkred]
    >>> c=C()
    >>> c.__dict__[/color][/color][/color]
    {}[color=blue][color=green][color=darkred]
    >>> c.__dict__['attr']=1
    >>> c.__dict__[/color][/color][/color]
    {'attr': 1}[color=blue][color=green][color=darkred]
    >>> c.attr[/color][/color][/color]
    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?

    --
    Denis S. Otkidach
    http://www.python.ru/ [ru]


  • John Roth

    #2
    Re: Inconsistent behavior of descriptors


    "Denis S. Otkidach" <ods@strana.r u> wrote in message
    news:mailman.10 64938610.30070. python-list@python.org ...[color=blue]
    >
    > I've noticed that the order of attribute lookup is inconsistent
    > when descriptor is used. property instance takes precedence of
    > instance attributes:
    >[color=green][color=darkred]
    > >>> class A(object):[/color][/color]
    > ... def _get_attr(self) :
    > ... return self._attr
    > ... attr = property(_get_a ttr)
    > ...[color=green][color=darkred]
    > >>> a=A()
    > >>> a.__dict__[/color][/color]
    > {}[color=green][color=darkred]
    > >>> a.__dict__['attr']=1
    > >>> a.__dict__[/color][/color]
    > {'attr': 1}[color=green][color=darkred]
    > >>> a.attr[/color][/color]
    > 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:
    >[color=green][color=darkred]
    > >>> class descr(object):[/color][/color]
    > ... def __get__(self, inst, cls):
    > ... return inst._attr
    > ...[color=green][color=darkred]
    > >>> class B(object):[/color][/color]
    > ... attr = descr()
    > ...[color=green][color=darkred]
    > >>> b=B()
    > >>> b.__dict__[/color][/color]
    > {}[color=green][color=darkred]
    > >>> b.__dict__['attr']=1
    > >>> b.__dict__[/color][/color]
    > {'attr': 1}[color=green][color=darkred]
    > >>> b.attr[/color][/color]
    > 1
    >
    > Subclasses of property behave like property itself:
    >[color=green][color=darkred]
    > >>> class descr2(property ):[/color][/color]
    > ... def __get__(self, inst, cls):
    > ... return inst._attr
    > ...[color=green][color=darkred]
    > >>> class C(object):[/color][/color]
    > ... attr = descr2()
    > ...[color=green][color=darkred]
    > >>> c=C()
    > >>> c.__dict__[/color][/color]
    > {}[color=green][color=darkred]
    > >>> c.__dict__['attr']=1
    > >>> c.__dict__[/color][/color]
    > {'attr': 1}[color=green][color=darkred]
    > >>> c.attr[/color][/color]
    > 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?[/color]

    I'm not entirely sure what you're describing, however, I
    notice that your descriptor object has a __get__ method,
    but no __set__ or __del__ methods. The lookup chain
    is quite different for descriptors with only __get__ methods
    than it is for descriptors for all three.

    In particular, it's intended that instance attributes should
    be ignored if there is a data descriptor (that is, a descriptor
    with both__get__ and __set__ methods.)

    I'm not sure if this applies to your problem.

    John Roth[color=blue]
    >
    > --
    > Denis S. Otkidach
    > http://www.python.ru/ [ru]
    >
    >[/color]


    Comment

    • Bengt Richter

      #3
      Re: Inconsistent behavior of descriptors

      On Tue, 30 Sep 2003 20:15:10 +0400 (MSD), "Denis S. Otkidach" <ods@strana.r u> wrote:
      [color=blue]
      >
      >I've noticed that the order of attribute lookup is inconsistent
      >when descriptor is used. property instance takes precedence of
      >instance attributes:
      >[color=green][color=darkred]
      >>>> class A(object):[/color][/color]
      >... def _get_attr(self) :
      >... return self._attr
      >... attr = property(_get_a ttr)
      >...[color=green][color=darkred]
      >>>> a=A()
      >>>> a.__dict__[/color][/color]
      >{}[color=green][color=darkred]
      >>>> a.__dict__['attr']=1
      >>>> a.__dict__[/color][/color]
      >{'attr': 1}[color=green][color=darkred]
      >>>> a.attr[/color][/color]
      >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:
      >[color=green][color=darkred]
      >>>> class descr(object):[/color][/color]
      >... def __get__(self, inst, cls):
      >... return inst._attr
      >...[color=green][color=darkred]
      >>>> class B(object):[/color][/color]
      >... attr = descr()
      >...[color=green][color=darkred]
      >>>> b=B()
      >>>> b.__dict__[/color][/color]
      >{}[color=green][color=darkred]
      >>>> b.__dict__['attr']=1
      >>>> b.__dict__[/color][/color]
      >{'attr': 1}[color=green][color=darkred]
      >>>> b.attr[/color][/color]
      >1
      >
      >Subclasses of property behave like property itself:
      >[color=green][color=darkred]
      >>>> class descr2(property ):[/color][/color]
      >... def __get__(self, inst, cls):
      >... return inst._attr
      >...[color=green][color=darkred]
      >>>> class C(object):[/color][/color]
      >... attr = descr2()
      >...[color=green][color=darkred]
      >>>> c=C()
      >>>> c.__dict__[/color][/color]
      >{}[color=green][color=darkred]
      >>>> c.__dict__['attr']=1
      >>>> c.__dict__[/color][/color]
      >{'attr': 1}[color=green][color=darkred]
      >>>> c.attr[/color][/color]
      >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?
      >[/color]
      I think (not having read the above carefully) that it's all documented in



      (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.__getatt ribute__ 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_Generi cGetAttr() in Objects/object.c.

      For classes, the machinery is in type.__getattri bute__ 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.__getatt ribute__(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.__getatt ribute__ and type.__getattri bute__ make different calls to __get__.
      data descriptors always override instance dictionaries.
      non-data descriptors may be overridden by instance dictionaries.
      """

      Regards,
      Bengt Richter

      Comment

      Working...