在Python中,为什么属性优先于实例属性?

时间:2018-12-16 10:36:17

标签: python oop properties descriptor

This article描述了Python执行o.a时如何在对象上查找属性。优先顺序很有趣-它寻找:

  1. 是数据描述符的类属性(最常见的是属性)
  2. 实例属性
  3. 其他任何班级属性

我们可以使用下面的代码确认这一点,该代码将创建一个对象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 }}会调用该属性)。

“天真的”查找顺序是否会引起问题?

3 个答案:

答案 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):

  
      
  1. 查看dict类型。如果找到数据描述符,请使用其get()方法产生结果。这将照顾诸如__dict____class__之类的特殊属性。
  2.   
  3. 查看实例字典。如果您找到任何东西,就是这样。 (这满足了通常情况下实例dict覆盖类dict的要求。)
  4.   
  5. 再次查找dict类型(实际上,实际上使用的是步骤1中保存的结果)。如果找到描述符,请使用其get()方法;如果您发现其他东西,就是这样;如果不存在,请引发AttributeError。
  6.   

总而言之,__dict____class__属性被实现为属性(数据描述符)。为了维持有效状态,实例字典不能覆盖__dict____class__。因此,属性(数据描述符)优先于实例属性。

答案 2 :(得分:1)

  

我想知道的是,为什么数据描述符优先于   实例属性?

如果没有某种方法比普通实例查找具有更高的优先级,那么您如何期望拦截实例属性访问?这些方法本身不能是实例属性,因为这会破坏它们的目的(我认为至少没有关于它们的约定)。

除了@timgeb的简洁评论外,我无法比官方Descriptor How To

更好地解释描述符