28

When i say "python attribute lookup proccess" i mean: what python does when you write x.foo??

Searching the web i didn't found to much docs about this, one of the best papers i found resumed the proccess to the following steps (you can see the full article here)

  1. If attrname is a special (i.e. Python-provided) attribute for objectname, return it.
  2. Check objectname._class_._dict_ for attrname. If it exists and is a data-descriptor, return the descriptor result. Search all bases of objectname._class_ for the same case.
  3. Check objectname._dict_ for attrname, and return if found. If objectname is a class, search its bases too. If it is a class and a descriptor exists in it or its bases, return the descriptor result.
  4. Check objectname._class_._dict_ for attrname. If it exists and is a non-data descriptor, return the descriptor result. If it exists, and is not a descriptor, just return it. If it exists and is a data descriptor, we shouldn't be here because we would have returned at point 2. Search all bases of objectname._class_ for same case.
  5. Raise AttributeError.

At first this might seem right, but the attribute lookup process is a little bit more complicated, for example for x.foo, it doesn't behave the same if x is a class or an instance.

I have a found some samples that can't be explained by this way. Consider the following python code:

class Meta(type): def __getattribute__(self, name): print("Metaclass getattribute invoked:", self) return type.__getattribute__(self, name) def __getattr__(self, item): print('Metaclass getattr invoked: ', item) return None class C(object, metaclass=Meta): def __getattribute__(self, name): print("Class getattribute invoked:", args) return object.__getattribute__(self, name) c=C() 

Now check the following lines with the corresponding output:

>> C.__new__ Metaclass getattribute invoked: <class '__main__.C'> <built-in method __new__ of type object at 0x1E1B80B0> >> C.__getattribute__ Metaclass getattribute invoked: <class '__main__.C'> <function __getattribute__ at 0x01457F18> >> C.xyz Metaclass getattribute invoked: <class '__main__.C'> Metaclass getattr invoked: xyz None >> c.__new__ Class getattribute invoked: (<__main__.C object at 0x013E7550>, '__new__') <built-in method __new__ of type object at 0x1E1B80B0> >> c.__getattribute__ Class getattribute invoked: (<__main__.C object at 0x01438DB0>, '__getattribute__') Metaclass getattribute invoked: <class '__main__.C'> <bound method C.__getattribute__ of <__main__.C object at 0x01438DB0>> >> 

The conclusions i have been are (considering we're searching for x.foo):

  • _getattribute_ is different for instances of < type 'type' > and < type 'object' >. For C.foo(), 'foo' is searched first on C._dict_ and returned if found (instead of searching type(C)) and for x.foo() 'foo' is searched on type(x)._dict_ and on x._dict_.
  • _getattribute_ method is always resolved on type(x), what i don't understand here is the last case: c._getattribute_, isn't object contains a method _getattribute_ (and C inherits from object), so why does metaclass getattribute method gets called.

Can someone explain this please?? or at less tell me where can i find some documentation about this, thanks.


See also: Why don't methods have reference equality?.

1
  • This blog post has a nice explanation. Commented May 20 at 8:39

1 Answer 1

4

If you added print("Metaclass getattribute invoked:", self, name) you'd see:

>>> c.__getattribute__ Class getattribute invoked: <__main__.C object at 0x2acdbb1430d0> __getattribute__ Metaclass getattribute invoked: <class '__main__.C'> __name__ <bound method C.__getattribute__ of <__main__.C object at 0x2acdbb1430d0>> 

The metaclass __getattribute__ is getting invoked in order to build the repr of the expression c.__getattribute__, so that it can print C's __name__.

btw, __getattribute__ works the same for classes and metaclasses; the attribute is looked up first on the instance then on the instance's type.

>>> Meta.foo = 1 >>> C.foo ('Metaclass getattribute invoked:', <class '__main__.C'>, 'foo') 1 >>> c.foo ('Class getattribute invoked:', <__main__.C object at 0x2acdbb1430d0>, 'foo') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 5, in __getattribute__ AttributeError: 'C' object has no attribute 'foo' >>> C.bar = 2 >>> c.bar ('Class getattribute invoked:', <__main__.C object at 0x2acdbb1430d0>, 'bar') 2 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.