python属性查找过程如何工作?

Ari*_*iel 24 python attributes python-datamodel

当我说"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. 提升AttributeError.

起初这可能看似正确,但属性查找过程稍微复杂一些,例如对于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()
Run Code Online (Sandbox Code Playgroud)

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

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

>> 
Run Code Online (Sandbox Code Playgroud)

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

  • __getattribute__对于<type'type'>和<type'object'>的实例是不同的.对于C.foo(),首先在C .__ dict__上搜索"foo",如果找到则返回(而不是搜索类型(C)),对于x.foo(),在类型(x).__ dict__上搜索"foo".在x .__ dict__.
  • __getattribute__方法总是在类型(x)上解析,这里我不明白的是最后一种情况:c .__ getattribute __,不是对象包含方法__getattribute __(而C继承自object),为什么metaclass getattribute方法得到调用.

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

eca*_*mur 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>>
Run Code Online (Sandbox Code Playgroud)

元类__getattribute__被调用以构建repr表达式的c.__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
Run Code Online (Sandbox Code Playgroud)