在 Python 中,为什么属性优先于实例属性?
In Python, why do properties take priority over instance attributes?
This article 描述了 Python 在执行 o.a
时如何查找对象的属性。优先顺序很有趣 - 它寻找:
- 作为数据描述符的 class 属性(最常见的是 属性)
- 实例属性
- 任何其他 class 属性
我们可以使用下面的代码来确认这一点,它创建了一个对象 o
和一个实例属性 a
,其 class 包含一个同名的 属性 :
class C:
def __init__(self):
self.__dict__['a'] = 1
@property
def a(self):
return 2
o = C()
print(o.a) # Prints 2
为什么 Python 使用此优先顺序而不是 "naive" 顺序(实例属性优先于所有 class 属性)? Python 的优先级顺序有一个明显的缺点:它使属性查找变慢,因为 Python 必须首先搜索,而不是只返回 o
的属性(如果存在) o
的 class 及其所有超级classes 用于数据描述符。
Python 的优先顺序有什么好处?大概不仅仅是上述情况,因为有一个实例变量和一个同名的 属性 是非常极端的情况(注意需要使用 self.__dict__['a'] = 1
来创建实例属性,因为通常 self.a = 1
会调用 属性).
是否存在 "naive" 查找顺序会导致问题的不同情况?
Guido van Rossum 本人(Python 的前 BDFL)在 2001 年 Python 2.2 引入新样式 类 时设计了此功能。讨论了推理在 PEP 252。明确提到对属性查找的影响:
This scheme has one drawback: in what I assume to be the most common case, referencing an instance variable stored in the instance dict, it does two dictionary lookups, whereas the classic scheme did a quick test for attributes starting with two underscores plus a single dictionary lookup.
并且:
A benchmark verifies that in fact this is as fast as classic instance variable lookup, so I'm no longer worried.
What I want to know is, why do data descriptors take priority over
instance attributes?
如果没有优先于正常实例查找的方法,您希望如何拦截实例属性访问?这些方法本身不能是实例属性,因为那样会破坏它们的目的(我认为至少没有关于它们的额外约定)。
除了@timgeb 的简明评论,关于描述符的任何解释我都比不上官方Descriptor How To
对于旧样式 类 (PEP 252):
The instance dict overrides the class dict, except for the special attributes (like __dict__
and __class__
), which have priority over the instance dict.
覆盖实例字典中的 __dict__
或 __class__
会破坏属性查找并导致实例以极其奇怪的方式运行。
在新式 类 中,Guido 选择了以下属性查找实现来保持一致性 (PEP 252):
- Look in the type dict. If you find a data descriptor, use its
get()
method to produce the result. This takes care of special attributes like __dict__
and __class__
.
- Look in the instance dict. If you find anything, that's it. (This takes care of the requirement that normally the instance dict overrides the class dict.)
- Look in the type dict again (in reality this uses the saved result from step 1, of course). If you find a descriptor, use its get() method; if you find something else, that's it; if it's not there, raise AttributeError.
总之,__dict__
和__class__
属性被实现为属性(数据描述符)。为了保持有效状态,实例字典不能覆盖 __dict__
和 __class__
。因此,属性(数据描述符)优先于实例属性。
This article 描述了 Python 在执行 o.a
时如何查找对象的属性。优先顺序很有趣 - 它寻找:
- 作为数据描述符的 class 属性(最常见的是 属性)
- 实例属性
- 任何其他 class 属性
我们可以使用下面的代码来确认这一点,它创建了一个对象 o
和一个实例属性 a
,其 class 包含一个同名的 属性 :
class C:
def __init__(self):
self.__dict__['a'] = 1
@property
def a(self):
return 2
o = C()
print(o.a) # Prints 2
为什么 Python 使用此优先顺序而不是 "naive" 顺序(实例属性优先于所有 class 属性)? Python 的优先级顺序有一个明显的缺点:它使属性查找变慢,因为 Python 必须首先搜索,而不是只返回 o
的属性(如果存在) o
的 class 及其所有超级classes 用于数据描述符。
Python 的优先顺序有什么好处?大概不仅仅是上述情况,因为有一个实例变量和一个同名的 属性 是非常极端的情况(注意需要使用 self.__dict__['a'] = 1
来创建实例属性,因为通常 self.a = 1
会调用 属性).
是否存在 "naive" 查找顺序会导致问题的不同情况?
Guido van Rossum 本人(Python 的前 BDFL)在 2001 年 Python 2.2 引入新样式 类 时设计了此功能。讨论了推理在 PEP 252。明确提到对属性查找的影响:
This scheme has one drawback: in what I assume to be the most common case, referencing an instance variable stored in the instance dict, it does two dictionary lookups, whereas the classic scheme did a quick test for attributes starting with two underscores plus a single dictionary lookup.
并且:
A benchmark verifies that in fact this is as fast as classic instance variable lookup, so I'm no longer worried.
What I want to know is, why do data descriptors take priority over instance attributes?
如果没有优先于正常实例查找的方法,您希望如何拦截实例属性访问?这些方法本身不能是实例属性,因为那样会破坏它们的目的(我认为至少没有关于它们的额外约定)。
除了@timgeb 的简明评论,关于描述符的任何解释我都比不上官方Descriptor How To
对于旧样式 类 (PEP 252):
The instance dict overrides the class dict, except for the special attributes (like
__dict__
and__class__
), which have priority over the instance dict.
覆盖实例字典中的 __dict__
或 __class__
会破坏属性查找并导致实例以极其奇怪的方式运行。
在新式 类 中,Guido 选择了以下属性查找实现来保持一致性 (PEP 252):
- Look in the type dict. If you find a data descriptor, use its
get()
method to produce the result. This takes care of special attributes like__dict__
and__class__
.- Look in the instance dict. If you find anything, that's it. (This takes care of the requirement that normally the instance dict overrides the class dict.)
- Look in the type dict again (in reality this uses the saved result from step 1, of course). If you find a descriptor, use its get() method; if you find something else, that's it; if it's not there, raise AttributeError.
总之,__dict__
和__class__
属性被实现为属性(数据描述符)。为了保持有效状态,实例字典不能覆盖 __dict__
和 __class__
。因此,属性(数据描述符)优先于实例属性。