python属性访问与数据描述

2024-10-04 03:24:50 发布

您现在位置:Python中文网/ 问答频道 /正文

我读过一些博客和文档,当访问实例属性obj.a时:

  1. 尝试访问当前类__dict__和基类^{dict}/strong>中的数据描述符(名为a
  2. obj.__dict__中查找a
  3. 在当前类__dict__和基类__dict__中查找非数据描述符(名为a
  4. 在当前类__dict__和基类__dict__中查找属性(名为a
  5. 如果有的话,打电话给__getattr__
  6. 提高AttributeError

但我发现此搜索规则与以下代码的行为不匹配:

class ADesc(object):
    def __init__(self, name):
        self._name = name

    def __get__(self, obj, objclass):
        print('get.....')
        return self._name + '  ' + str(obj) + '  ' + str(objclass)

    def __set__(self, obj, value):
        print('set.....')
        self._name = value


class A(object):
    dd_1 = ADesc('dd_1 in A')


class B(A):
    dd_1 = 'dd_1 in B'


if __name__ == '__main__':
    print(A.__dict__)
    # {'dd_1': <__main__.ADesc object at 0x10ed0d050>, '__dict__': <attribute '__dict__' of 'A' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}

    print(B.__dict__)
    # {'dd_1': 'dd_1 in B', '__module__': '__main__', '__doc__': None}

    b = B()
    print(b.dd_1)  # dd_1 in B

我认为最后一个print(b.dd_1)将调用ADesc中的__get__,因为根据第一条规则,基类A__dict__包含我们正在访问的属性dd_1,所以应该调用数据描述符。那么,上面的访问规则是错误的还是这里涉及到了其他魔法?你知道吗


Tags: 数据nameinselfobj属性main规则
2条回答

你误解了描述符是如何在类中找到的。Python将在类层次结构中使用第一个这样的名称。一旦找到,搜索就会停止。B.dd_1存在,因此不考虑A.dd_1。你知道吗

文档告诉您关于B没有定义dd_1的基类;在这种情况下,搜索B,然后搜索A。但是当B具有属性dd_1时,任何进一步的搜索都将停止。你知道吗

注意,搜索顺序是由类MRO(方法解析顺序)设置的。不应区分类__dict__中的搜索和基类的__dict__属性,而应将搜索视为:

def find_class_attribute(cls, name):
    for c in cls.__mro__:
        if name in c.__dict__:
            return c.__dict__[name]

MRO(由^{} attribute体现)包括当前类对象:

>>> B.__mro__()
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)

相关文档位于datamodel reference;其中自定义类说明:

Class attribute references are translated to lookups in this dictionary, e.g., C.x is translated to C.__dict__["x"] (although there are a number of hooks which allow for other means of locating attributes). When the attribute name is not found there, the attribute search continues in the base classes.

实例属性的实际实现如下:

  • 类位于(type(instance)
  • __getattribute__方法被调用(type(instance).__getattribute__(instance, name)
  • __getattribute__扫描MRO以查看类及其基类上是否存在名称(find_class_attribute(self, name)
    • 如果存在这样一个对象,并且它是一个数据描述符(具有__set____delete__方法),则使用该对象,搜索停止。你知道吗
    • 如果有这样一个对象,但它不是数据描述符,则会保留一个引用供以后使用。你知道吗
  • __getattribute__instance.__dict__中查找名称
    • 如果有这样的物体,搜索就会停止。使用实例属性。你知道吗
  • 找不到数据描述符,实例dict中也没有属性。但是通过MRO的搜索可能找到了一个非数据描述符对象
    • 如果在MRO中找到了一个对象的引用,则使用它并停止搜索。你知道吗
  • 如果类(或基类)上定义了__getattr__方法,则调用该方法并使用结果。搜索停止。你知道吗
  • 引发AttributeError。你知道吗

在类和基类中没有三个单独的搜索。(而且,它不仅仅是类和它的基;它是整个MRO。)在MRO中有一个搜索,它一找到什么就停止,不管找到的对象可能支持或不支持描述符协议的哪些部分。你知道吗

当对b.dd_1的搜索找到'dd_1 in B'时,它停止MRO搜索。它不会因为'dd_1 in B'不是一个描述符就一直寻找。你知道吗

以下是标准属性解析逻辑的正确版本,在object.__getattribute__中实现。(这只是object.__getattribute__;它不包括具有自己的__getattribute____getattr__的类。)

  1. 在对象的MRO中搜索与属性名匹配的dict条目。你知道吗
  2. 如果MRO搜索找到一个带有__get__方法的数据描述符,停止搜索并使用该描述符。你知道吗
  3. 否则,我们检查实例dict。如果有与属性名匹配的条目,请停止并使用该条目。如果没有匹配的条目(或者没有实例dict),请继续。你知道吗
  4. 如果第2步搜索找到非数据描述符或非描述符,请使用该描述符。你知道吗
  5. 否则,查找失败。引发AttributeError。你知道吗

相关问题 更多 >