我读过一些博客和文档,当访问实例属性obj.a
时:
__dict__
和基类^{dict}/strong>中的数据描述符(名为a
)obj.__dict__
中查找a
__dict__
和基类__dict__
中查找非数据描述符(名为a
)__dict__
和基类__dict__
中查找属性(名为a
)__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)
将调用ADesc
中的__get__
,因为根据第一条规则,基类A
的__dict__
包含我们正在访问的属性dd_1
,所以应该调用数据描述符。那么,上面的访问规则是错误的还是这里涉及到了其他魔法?你知道吗
你误解了描述符是如何在类中找到的。Python将在类层次结构中使用第一个这样的名称。一旦找到,搜索就会停止。
B.dd_1
存在,因此不考虑A.dd_1
。你知道吗文档告诉您关于
B
没有定义dd_1
的基类;在这种情况下,搜索B
,然后搜索A
。但是当B
具有属性dd_1
时,任何进一步的搜索都将停止。你知道吗注意,搜索顺序是由类MRO(方法解析顺序)设置的。不应区分类
__dict__
中的搜索和基类的__dict__
属性,而应将搜索视为:MRO(由^{} attribute 体现)包括当前类对象:
相关文档位于datamodel reference;其中自定义类说明:
实例属性的实际实现如下:
type(instance)
)__getattribute__
方法被调用(type(instance).__getattribute__(instance, name)
)__getattribute__
扫描MRO以查看类及其基类上是否存在名称(find_class_attribute(self, name)
)__set__
或__delete__
方法),则使用该对象,搜索停止。你知道吗__getattribute__
在instance.__dict__
中查找名称__getattr__
方法,则调用该方法并使用结果。搜索停止。你知道吗AttributeError
。你知道吗在类和基类中没有三个单独的搜索。(而且,它不仅仅是类和它的基;它是整个MRO。)在MRO中有一个搜索,它一找到什么就停止,不管找到的对象可能支持或不支持描述符协议的哪些部分。你知道吗
当对
b.dd_1
的搜索找到'dd_1 in B'
时,它停止MRO搜索。它不会因为'dd_1 in B'
不是一个描述符就一直寻找。你知道吗以下是标准属性解析逻辑的正确版本,在
object.__getattribute__
中实现。(这只是object.__getattribute__
;它不包括具有自己的__getattribute__
或__getattr__
的类。)__get__
方法的数据描述符,停止搜索并使用该描述符。你知道吗相关问题 更多 >
编程相关推荐