10

Given:

In [37]: class A:
   ....:     f = 1
   ....:

In [38]: class B(A):
   ....:     pass
   ....:

In [39]: getattr(B, 'f')
Out[39]: 1

Okay, that either calls super or crawls the mro?

In [40]: getattr(A, 'f')
Out[40]: 1

This is expected.

In [41]: object.__getattribute__(A, 'f')
Out[41]: 1

In [42]: object.__getattribute__(B, 'f')
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-42-de76df798d1d> in <module>()
----> 1 object.__getattribute__(B, 'f')

AttributeError: 'type' object has no attribute 'f'

What is getattribute not doing that getattr does?

In [43]: type.__getattribute__(B, 'f')
Out[43]: 1

What?! type.__getattribute__ calls super but object's version doesn't?

In [44]: type.__getattribute__(A, 'f')
Out[44]: 1
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Neil G
  • 32,138
  • 39
  • 156
  • 257

1 Answers1

12

You are operating directly on classes. object.__getattribute__ is only used on instances of A and B instead. That's because special methods are looked up on the type; for instances the type is the class.

For classes then, the type is.. type:

>>> class A:
...     f = 1
... 
>>> class B(A):
...     pass
... 
>>> type(B)
<class 'type'>

so type.__getattribute__ is used:

>>> type.__getattribute__(B, 'f')
1

and object.__getattribute__ works fine on instances:

>>> object.__getattribute__(B(), 'f')
1

For instances attributes are looked up first on the class (in the case of data descriptors), then on the instance, then if the instance doesn't have the attribute, the class hierarchy is searched in MRO order. This is the job of object.__getattribute__. So object.__getattribute__ looks at the first argument (e.g. self, the instance object) for the attribute, and at objects in type(self).__mro__.

For classes, attributes are looked up on the class itself and all its bases; type.__getattribute__ looks directly at self.__mro__ for these; self being a class object here.

If you use object.__getattribute__ for classes then, there is no f attribute on B directly, and no f anywhere in type(B).__mro__. If you use type.__getattribute__, A is a member of B.__mro__ so f is found there:

>>> type(B).__mro__
(<class 'type'>, <class 'object'>)
>>> B.__mro__
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 3
    Would you mind explaining why object.__getattribute__(A, 'f') works then? I'm a little confused. – Magitrek Jun 10 '14 at 21:23
  • I'm sure you're right, but I'm still a bit lost. What is `object.__getattribute__(B, 'f')` doing? Is it necessarily bad code? It might solve my other problem: http://stackoverflow.com/questions/24150636/class-properties-without-superclass-properties – Neil G Jun 10 '14 at 21:24
  • 1
    @Magitrek: I was getting there. Done now. `object.__getattribute__` looks for `'f'` in `self` and each of `type(self).__mro__`. For class `B`, `type(B).__mro__` is not the same thing as `B.__mro__`. – Martijn Pieters Jun 10 '14 at 21:25
  • 1
    @NeilG: `object.__getattribute__` is the *wrong method* for looking at attributes on a class hierachy. Use `type.__getattribute__` instead. Or better still, just use `type(B).__getattribute__` directly and get the right special method, always. – Martijn Pieters Jun 10 '14 at 21:32
  • @MartijnPieters: Thank you. I want to know whether `B` has the attribute itself or whether it's being inherited. Is that impossible? – Neil G Jun 10 '14 at 21:34
  • @NeilG: I posted an answer on your other question too. – Martijn Pieters Jun 10 '14 at 21:35
  • 2
    So when accessing `X.attr`, `object.__getattribute__` searches `X.__dict__` -> `type(X).__mro__.__dict__`, while `type.__getattribute__` searches `X.__dict__` -> `X.__mro__.__dict__` -> `type(X).__mro__.__dict__`. Am I right? – nalzok Jul 28 '16 at 02:30
  • 2
    @sunqingyao: yeah; `object.__getattribute__` is for instances, `type.__getattribute__` for classes, and the latter must look for attributes on the metaclass hierarchy too. – Martijn Pieters Jul 28 '16 at 10:49