This article描述了Python执行o.a
时如何在对象上查找属性。优先顺序很有趣-它寻找:
我们可以使用下面的代码确认这一点,该代码将创建一个对象o
,其实例属性为a
,其类包含相同名称的属性:
class C:
def __init__(self):
self.__dict__['a'] = 1
@property
def a(self):
return 2
o = C()
print(o.a) # Prints 2
为什么Python使用此优先级顺序而不是“天真”顺序(实例属性优先于所有类属性)? Python的优先级顺序有一个很大的缺点:它会使属性查找变慢,因为Python必须首先搜索o
的类o(如果存在)(常见的情况)。 >及其所有超类作为数据描述符。
Python优先顺序的好处是什么?大概不仅仅适用于上述情况,因为拥有一个实例变量和一个同名的属性几乎是一个极端情况(请注意,需要使用self.__dict__['a'] = 1
来创建实例属性,因为通常的{{1 }}会调用该属性)。
“天真的”查找顺序是否会引起问题?
答案 0 :(得分:4)
Guido van Rossum本人(Python的前BDFL)本人在2001年用Python 2.2引入新型类时设计了此功能。PEP 252中讨论了推理的问题。明确提到了对属性查找的影响:
此方案有一个缺点:在我认为是最常见的情况下,引用存储在实例字典中的实例变量,它会进行两次字典查找,而经典方案对以两个下划线开头的属性进行了快速测试。加上一个字典查找。
并且:
一个基准测试验证了它实际上与经典实例变量查找一样快,因此我不再担心。
答案 1 :(得分:2)
对于旧式类(PEP 252):
除了特殊属性(例如
__dict__
和__class__
)外,实例dict覆盖类dict,而优先于实例dict的特殊属性。
在实例字典中覆盖__dict__
或__class__
会破坏属性查找,并导致实例以极其奇怪的方式运行。
在新型类中,Guido选择了以下属性查找实现以保持一致性(PEP 252):
- 查看dict类型。如果找到数据描述符,请使用其
get()
方法产生结果。这将照顾诸如__dict__
和__class__
之类的特殊属性。- 查看实例字典。如果您找到任何东西,就是这样。 (这满足了通常情况下实例dict覆盖类dict的要求。)
- 再次查找dict类型(实际上,实际上使用的是步骤1中保存的结果)。如果找到描述符,请使用其get()方法;如果您发现其他东西,就是这样;如果不存在,请引发AttributeError。
总而言之,__dict__
和__class__
属性被实现为属性(数据描述符)。为了维持有效状态,实例字典不能覆盖__dict__
和__class__
。因此,属性(数据描述符)优先于实例属性。
答案 2 :(得分:1)
我想知道的是,为什么数据描述符优先于 实例属性?
如果没有某种方法比普通实例查找具有更高的优先级,那么您如何期望拦截实例属性访问?这些方法本身不能是实例属性,因为这会破坏它们的目的(我认为至少没有关于它们的约定)。
除了@timgeb的简洁评论外,我无法比官方Descriptor How To
更好地解释描述符