首页 > 解决方案 > 使用数据描述符访问 python 属性

问题描述

我已经阅读了一些访问实例属性时的博客和文档obj.a

  1. a尝试访问当前类和基类 __dict__中的数据描述符(命名)__dict__
  2. 找到a_obj.__dict__
  3. a在当前类__dict__和基类中查找非数据描述符(命名)__dict__
  4. a在当前类__dict__和基类中查找属性(命名)__dict__
  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)将调用__get__in ADesc,因为根据第一条规则,__dict__基类A包含我们正在访问的属性dd_1,因此应该调用数据描述符。那么上面的访问规则是错误的还是这里涉及的任何其他魔法?

标签: pythoninstance-variables

解决方案


您误解了如何在类中找到描述符。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(由cls.__mro__属性体现)包括当前类对象:

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

相关文档可在数据模型参考中找到;自定义指出:

类属性引用被翻译成这个字典中的查找,例如,C.x被翻译成C.__dict__["x"](尽管有许多钩子允许其他定位属性的方法)。如果在那里找不到属性名称,则在基类中继续进行属性搜索。

实例属性的实际实现是这样的:

  • 班级位于 ( type(instance))
  • __getattribute__方法称为 ( type(instance).__getattribute__(instance, name))
  • __getattribute__扫描 MRO 以查看该名称是否存在于该类及其基类 ( find_class_attribute(self, name))
    • 如果存在这样的对象,并且它是一个数据描述符(具有__set__or__delete__方法),则使用该对象,并且停止搜索。
    • 如果存在这样的对象但它不是数据描述符,则保留参考以供以后使用。
  • __getattribute__在中查找名称instance.__dict__
    • 如果有这样的对象,搜索就会停止。使用实例属性。
  • 没有找到数据描述符,并且实例字典中没有属性。但是通过 MRO 的搜索可能已经找到了一个非数据描述符对象
    • 如果在 MRO 中找到对对象的引用,则使用该对象并停止搜索。
  • 如果__getattr__在类(或基类)上定义了方法,则调用它,并使用结果。搜索停止。
  • anAttributeError被提出。

推荐阅读