问题
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)
- If attrname is a special (i.e. Python-provided) attribute for objectname, return it.
- 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.
- 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.
- 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.
- 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.
回答1:
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
来源:https://stackoverflow.com/questions/11211233/how-python-attribute-lookup-process-works