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

How python attribute lookup process works?

当我说"python属性查找过程"时,我的意思是:编写x.foo时,python会做什么??

在网上搜索我没有找到很多关于这方面的文档,我找到的最好的一篇论文继续进行以下步骤(您可以在这里看到完整的文章)

  • 如果attrname是objectname的一个特殊(即python提供的)属性,则返回它。
  • 检查objectname.uu class_uuu.uuu dict_uuu,查看attrname。如果它存在并且是数据描述符,则返回描述符结果。搜索objectname.u类的所有基以查找同一个事例。
  • 检查objectname.uu dict_uuuuuuuuuu,查看attrname,找到后返回。如果objectname是一个类,也可以搜索它的基。如果它是一个类,并且其中或其基中存在描述符,则返回描述符结果。
  • 检查objectname.uu class_uuu.uuu dict_uuu,查看attrname。如果它存在并且是非数据描述符,则返回描述符结果。如果它存在,并且不是描述符,只需返回它。如果它存在并且是一个数据描述符,我们不应该出现在这里,因为我们会返回到第2点。搜索objectname.u类的所有基以查找同一个事例。
  • 引发属性错误。
  • 起初,这看起来似乎是正确的,但是属性查找过程有点复杂,例如对于x.foo,如果x是一个类或实例,它的行为就不一样。

    我找到了一些不能用这种方法解释的样品。考虑下面的python代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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()

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    >> 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_uuu是不同的。对于c.foo(),首先在c."uu dict"上搜索"foo",如果找到则返回"foo"(而不是搜索类型(c)),对于x.foo(),在类型(x)上搜索"foo"。在"uu dict"和"x"上搜索"foo"。
    • _ getattribute_uuu方法总是在类型(x)上解析,我不理解的是最后一种情况:c."getattribute"不是对象包含方法"getattribute"(和c从对象继承),所以为什么要调用元类getattribute方法。

    有人能解释一下吗?或者至少告诉我在哪里可以找到关于这个的文档,谢谢。


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

    1
    2
    3
    4
    >>> 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__是为了构建表达式c.__getattribute__repr以便可以打印C__name__

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    >>> 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