What is the difference between type.__getattribute__ and object.__getattribute__?
Asked Answered
C

1

11

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
Cipolin answered 10/6, 2014 at 21:10 Comment(1)
@thecoder16: Python 3.Cipolin
T
13

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'>)
Term answered 10/6, 2014 at 21:21 Comment(10)
Would you mind explaining why object.__getattribute__(A, 'f') works then? I'm a little confused.Rigatoni
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: #24151136Cipolin
@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__.Term
@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.Term
@MartijnPieters: Thank you. I want to know whether B has the attribute itself or whether it's being inherited. Is that impossible?Cipolin
@NeilG: I posted an answer on your other question too.Term
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?Elysium
@sunqingyao: yeah; object.__getattribute__ is for instances, type.__getattribute__ for classes, and the latter must look for attributes on the metaclass hierarchy too.Term
"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. " IMO that statement isn't completely accurate. Data descriptors are looked for on the entire MRO, not just the class. Also if instance doesn't have attribute, first non-data descriptors are searched on the entire MRO, then attributes.Posse
"For classes, attributes are looked up on the class itself and all its bases;" Technically data descriptors on metaclass is looked up first. Also non-data descriptors are looked up before attributes on basesPosse

© 2022 - 2024 — McMap. All rights reserved.