为什么从 class 和实例中获取属性的查找过程不同?
Why are the lookup procedures for getting an attribute from a class and from an instance different?
Python in a Nutshell 描述了获取属性时的查找过程。书中区分了两种情况
从 class 获取属性时的查找过程,例如cls.name
Getting an attribute from a class
When you use the syntax C.name
to refer to an attribute
on a class object C
, the lookup proceeds in two steps:
When name
is a key in C.__dict__
, C.name
fetches the value v
from C.__dict__['name']
. Then, when v
is
a descriptor (i.e., type(v)
supplies a method named
__get__
), the value of C.name
is the result of calling
type(v).__get__(v, None, C)
. When v
is not a descriptor,
the value of C.name is v
.
When name
is not a key in C.__dict__
, C.name
delegates the lookup to C
’s base classes, meaning it loops on C
’s
ancestor classes and tries the name lookup on each (in method
resolution order, as covered in “Method resolution order” on page
113).
因为在 Python 3 中,每个 class 对象实际上是其元 class 的一个实例(例如 type
class),根据book,为什么从 class 获取属性的查找过程和从实例获取属性的查找过程不同?
它们差别不大,书中的描述涵盖了它们的两个不同之处:
- 在 class 实例上找到的描述符(在 class 上找不到)不会被调用(
a.x = somedescriptor()
其中 a
是 class 实例,而不是 class,后跟 a.x
将只是 return 你刚刚创建的 somedescriptor()
的实例),而在元 class 上找到的描述符实例即 class(在 metaclass 上找不到之后)被调用 None
作为它被调用的实例(A.x = somedescriptor()
其中 A
是metaclass 实例,而不是 metaclass,将在您刚刚创建的 somedescriptor()
上调用 .__get__(None, A)
)。这允许像 @classmethod
这样的东西通过将方法绑定到 class 本身来工作,无论它是在 class 的实例还是 class 本身的实例上查找。
- Class 实例没有“父实例”的概念(class 实例本身的命名空间是一个扁平的
dict
,即使与之关联的属性class 实例由来自多个继承级别的方法定义),因此基于 MRO 的查找的想法是元class 实例所独有的。
其他的都差不多,只是这本书在这里掩盖了元class的概念,因为大多数class都是基[=21=的实例],它没有特殊的行为。如果您有 type
以外的元 class,则在 class 上查找属性时应用完整的实例查找规则(它只是 [=37= 的 class ] 是元class).
他们可能在早期试图避免 metaclasses 的复杂性,但在这个过程中使得实例查找规则似乎不适用于 classes;他们这样做,只是 classes 在基本查找过程中添加了一些额外的规则。
Python in a Nutshell 描述了获取属性时的查找过程。书中区分了两种情况
从 class 获取属性时的查找过程,例如
cls.name
Getting an attribute from a class
When you use the syntax
C.name
to refer to an attribute on a class objectC
, the lookup proceeds in two steps:When
name
is a key inC.__dict__
,C.name
fetches the valuev
fromC.__dict__['name']
. Then, whenv
is a descriptor (i.e.,type(v)
supplies a method named__get__
), the value ofC.name
is the result of callingtype(v).__get__(v, None, C)
. Whenv
is not a descriptor, the value ofC.name is v
.When
name
is not a key inC.__dict__
,C.name
delegates the lookup toC
’s base classes, meaning it loops onC
’s ancestor classes and tries the name lookup on each (in method resolution order, as covered in “Method resolution order” on page 113).
因为在 Python 3 中,每个 class 对象实际上是其元 class 的一个实例(例如 type
class),根据book,为什么从 class 获取属性的查找过程和从实例获取属性的查找过程不同?
它们差别不大,书中的描述涵盖了它们的两个不同之处:
- 在 class 实例上找到的描述符(在 class 上找不到)不会被调用(
a.x = somedescriptor()
其中a
是 class 实例,而不是 class,后跟a.x
将只是 return 你刚刚创建的somedescriptor()
的实例),而在元 class 上找到的描述符实例即 class(在 metaclass 上找不到之后)被调用None
作为它被调用的实例(A.x = somedescriptor()
其中A
是metaclass 实例,而不是 metaclass,将在您刚刚创建的somedescriptor()
上调用.__get__(None, A)
)。这允许像@classmethod
这样的东西通过将方法绑定到 class 本身来工作,无论它是在 class 的实例还是 class 本身的实例上查找。 - Class 实例没有“父实例”的概念(class 实例本身的命名空间是一个扁平的
dict
,即使与之关联的属性class 实例由来自多个继承级别的方法定义),因此基于 MRO 的查找的想法是元class 实例所独有的。
其他的都差不多,只是这本书在这里掩盖了元class的概念,因为大多数class都是基[=21=的实例],它没有特殊的行为。如果您有 type
以外的元 class,则在 class 上查找属性时应用完整的实例查找规则(它只是 [=37= 的 class ] 是元class).
他们可能在早期试图避免 metaclasses 的复杂性,但在这个过程中使得实例查找规则似乎不适用于 classes;他们这样做,只是 classes 在基本查找过程中添加了一些额外的规则。