[Python-Dev] Attribute lookup (was Re: python-dev Summary for 2003-05-01 through 2003-05-15)

Bjorn Pettersen BPettersen@NAREX.com
Tue, 20 May 2003 00:25:26 -0600


> From: Phillip J. Eby [mailto:pje@telecommunity.com]=20
>=20
> At 09:58 PM 5/18/03 -0400, Aahz wrote:
> > [Normally I send my corrections to Brett privately, but=20
> > since I'm taking a whack at attribute lookup, I figured=20
> > this ought to be public.]
> >
> >On Sun, May 18, 2003, Brett C. wrote:
> > >
> > > The only thing I would like help with this summary is if=20
> > > someone knows the attribute lookup order (instance,=20
> > > class, class descriptor, ...)

[...]

> >This gets real tricky.  For simple attributes of an=20
> >instance, the order is instance, class/type, and base=20
> >classes of the class/type (but *not* the metaclass). =20
> >However, method resolution of the special methods goes
> >straight to the class.  Finally, if an attribute is found on the
> >instance, a search goes through the hierarchy to see whether a set
> >descriptor overrides (note specifically that it's a set descriptor;
> >methods are implemented using get descriptors).
> >
> >I *think* I have this right, but I'm sure someone will=20
> >correct me if I'm wrong.
>=20
> Here's the algorithm in a bit more detail:
>=20
> 1. First, the class/type and its bases are searched, checking=20
> dictionaries only.
>=20
> 2. If the object found is a "data descriptor"  (i.e. has a=20
> type with a non-null tp_descr_set pointer, which is closely=20
> akin to whether the descriptor has a '__set__' attribute),=20
> then the data descriptor's __get__ method is invoked.
>=20
> 3. If the object is not found, or not a data descriptor, the=20
> instance dictionary is checked.  If the attribute isn't in the=20
> instance dictionary, then the descriptor's __get__ method is=20
> invoked (assuming a descriptor was found).
>=20
> 4. Invoke __getattr__ if present.
>=20
> (Note that replacing __getattribute__ *replaces* this entire=20
> algorithm.)
>=20
> Also note that special methods are *not* handled specially here. =20
> The behavior Aahz is referring to is that slots (e.g. tp_call) on=20
> new-style types do not retrieve an instance attribute; they are=20
> based purely on class-level data.
[...]

Wouldn't that be explicitly specified class-level data, i.e. it
circumvents the __getattr__ hook completely:

>>> class C(object):
...   def __getattr__(self, attr):
...     if attr =3D=3D '__len__':
...        return lambda:42
...
>>> c =3D C()
>>> len(c)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: len() of unsized object

this makes it impossible to implement a __getattr__ anywhere that
intercepts len(obj):

>>> class meta(type):
...   def __getattr__(self, attr):
...     if attr =3D=3D '__len__':
...        return lambda:42
...
>>> class C(object):
...   __metaclass__ =3D meta
...
>>> C.__len__()
42
>>> c =3D C()
>>> len(c)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: len() of unsized object
>>> len(C)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: len() of unsized object

The meta example would have to work to be able to create "true" proxy
objects(?)

Is this intended behaviour?

-- bjorn