我阅读了一些博客和文档,这些博客和文档在访问实例属性obj.a
时:
a
和基类__dict __ __dict__
)
a
中找到obj.__dict__
a
和基类__dict__
中查找非数据描述符(命名为__dict__
)a
和基类__dict__
中查找属性(名为__dict__
)__getattr__
(如果有)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__
中的ADesc
,因为根据第一条规则,基类__dict__
的{{1}}包含属性我们正在访问的A
,因此应该调用数据描述符。那么上述访问规则是错误的还是这里涉及到其他魔术?
答案 0 :(得分:2)
在类和基类中没有三个单独的搜索。 (此外,它不仅是类及其基础;还是整个MRO。)通过MRO进行一次搜索,一旦找到内容,它就会停止,无论所找到的对象可能在描述符协议的哪个部分,也可能不支持。
当对b.dd_1
的搜索找到'dd_1 in B'
时,它将停止MRO搜索。它并不会一直只是因为'dd_1 in B'
不是描述符。
这是在object.__getattribute__
中实现的标准属性解析逻辑的正确版本。 (这只是object.__getattribute__
;它不包括具有自己的__getattribute__
或__getattr__
的类。)
__get__
方法找到了数据描述符,请停止搜索并使用该描述符。答案 1 :(得分:1)
您误解了如何在类中找到描述符。 Python将在类层次结构中使用 first 这样的名称。找到后,搜索将停止。 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__
attribute体现)包括当前的类对象:
>>> B.__mro__()
(<class '__main__.B'>, <class '__main__.A'>, <class 'object'>)
相关文档位于datamodel reference中;其中自定义类指出:
将类属性引用转换为此字典中的查找,例如,
C.x
被转换为C.__dict__["x"]
(尽管有许多允许使用其他方法定位属性的钩子)。 在此处找不到属性名称时,将继续在基类中搜索属性。
实例属性的实际实现如下:
type(instance)
)__getattribute__
的类type(instance).__getattribute__(instance, name)
方法__getattribute__
扫描MRO以查看该类及其基类(find_class_attribute(self, name)
)上是否存在该名称。
__set__
或__delete__
方法),则使用该对象,并停止搜索。__getattribute__
在instance.__dict__
中查找名称
__getattr__
方法,则将调用该方法并使用结果。搜索停止。AttributeError
。