25

当我说“python 属性查找过程”时,我的意思是:当你写 x.foo 时,python 做了什么?

在网上搜索我没有找到太多关于此的文档,我发现的最好的论文之一将过程恢复到以下步骤(您可以在此处查看完整文章)

  1. 如果 attrname 是 objectname 的特殊(即 Python 提供的)属性,则返回它。
  2. 检查 objectname.__class__.__dict__ 的 attrname。如果存在并且是数据描述符,则返回描述符结果。搜索 objectname.__class__ 的所有基以查找相同的案例。
  3. 检查 objectname.__dict__ 的 attrname,如果找到则返回。如果 objectname 是一个类,也搜索它的基类。如果它是一个类并且在它或其基中存在描述符,则返回描述符结果。
  4. 检查 objectname.__class__.__dict__ 的 attrname。如果存在且是非数据描述符,则返回描述符结果。如果它存在并且不是描述符,则返回它。如果它存在并且是一个数据描述符,我们就不应该在这里,因为我们会在第 2 点返回。搜索 objectname.__class__ 的所有基以查找相同的情况。
  5. 引发属性错误。

起初这似乎是正确的,但属性查找过程有点复杂,例如对于 x.foo,如果 x 是类或实例,它的行为就不一样了。

我发现了一些无法通过这种方式解释的样本。考虑以下python代码:

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()

现在检查以下行与相应的输出:

>> 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>>

>> 

我的结论是(考虑到我们正在搜索 x.foo):

  • __getattribute__ 对于 < type 'type' > 和 < type 'object' > 的实例是不同的。对于 C.foo(),首先在 C.__dict__ 上搜索 'foo',如果找到则返回(而不是搜索 type(C)),对于 x.foo(),在 type(x).__dict__ 上搜索 'foo' 和在 x.__dict__ 上。
  • __getattribute__ 方法总是在 type(x) 上解析,我不明白的是最后一种情况:c.__getattribute__, is not object contains a method __getattribute__ (and C继承 from object),那么为什么元类getattribute方法得到叫。

有人可以解释一下吗?或者告诉我在哪里可以找到有关此的一些文档,谢谢。

4

1 回答 1

4

如果您添加print("Metaclass getattribute invoked:", self, name),您会看到:

>>> 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>>

为了构建表达式,元类__getattribute__被调用,以便它可以打印。reprc.__getattribute__C__name__

顺便说一句,__getattribute__类和元类的工作方式相同;首先在实例上查找属性,然后在实例的类型上查找。

>>> 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
于 2012-06-26T16:19:58.583 回答