Why are the lookup procedures for getting an attribute from a class and from an instance different?(为什么从类和实例中获取属性的查找过程不同?)
问题描述
Python in a Nutshell 描述了获取属性时的查找过程.本书区分了两种情况
Python in a Nutshell describes the lookup procedures when getting an attribute. The book distinguishes two cases
从类中获取属性时的查找过程,例如
cls.name
从类中获取属性
当您使用语法 C.name
来引用属性时在类对象 C
上,查找分两步进行:
When you use the syntax C.name
to refer to an attribute
on a class object C
, the lookup proceeds in two steps:
当
name
是C.__dict__
中的键时,C.name
获取值v
来自C.__dict__['name']
.那么,当v
是一个描述符(即type(v)
提供一个名为__get__
),C.name
的值是调用的结果type(v).__get__(v, None, C)
.当v
不是描述符时,C.name 的值为 v
.
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
.
当 name
不是 C.__dict__
中的键时,C.name
将查找委托给 C
的基类,这意味着它在 C
上循环祖先类并尝试对每个类进行名称查找(在方法中解决顺序,如页面上的方法解决顺序"中所述113).
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).
从实例获取属性时的查找过程,例如obj.name
由于在 Python 3 中,每个类对象实际上都是其元类的一个实例(例如 type
类),根据本书,为什么从类中获取属性的查找过程和从不同实例中获取属性的查找过程?
Since in Python 3, every class object is actually an instance of its metaclass (e.g. type
class), according to the book, why are the lookup procedure for getting an attribute from a class and the lookup procedure for getting an attribute from an instance different?
推荐答案
它们并没有非常不同,书中的描述涵盖了它们不同的两种方式:
They're not very different, and the description from the book covers the two ways in which they differ:
- 在类实例上找到的描述符(在类上找不到之后)不会被调用(
ax = somedescriptor()
其中a
是一个类实例,不是一个类,后跟ax
只会返回你刚刚创建的somedescriptor()
的实例),而在元类实例上找到的描述符,即一个类(在没有找到之后在元类上)以None
作为它被调用的实例调用(Ax = somedescriptor()
其中A
是元类实例,而不是一个元类,将在您刚刚创建的somedescriptor()
上调用.__get__(None, A)
).这允许像@classmethod
这样的东西通过将方法绑定到类本身来工作,无论它是在类的实例上还是在类本身上查找. - 类实例没有父实例"的概念.(类实例本身的命名空间是一个扁平的
dict
,即使与该类实例关联的属性是由多个继承级别的方法定义的),因此基于MRO的查找的想法是独一无二的到元类实例.
- Descriptors found on a class instance (after not being found on the class) don't get invoked (
a.x = somedescriptor()
wherea
is a class instance, not a class, followed bya.x
will just return the instance ofsomedescriptor()
you just made), while descriptors found on a metaclass instance i.e. a class (after not being found on the metaclass) get invoked withNone
as the instance it was called on (A.x = somedescriptor()
whereA
is a metaclass instance, not a metaclass, will invoke.__get__(None, A)
on thesomedescriptor()
you just made). This allows stuff like@classmethod
to work by binding the method to the class itself whether it's looked up on an instance of the class or the class itself. - Class instances don't have a concept of "parent instances" (the namespace of the class instance itself is a flat
dict
, even if the attributes associated with that class instance were defined by methods from multiple levels of inheritance), so the idea of MRO-based lookup is unique to metaclass instances.
其他一切都差不多,只是本书在这里掩盖了元类的概念,因为大多数类都是基本 type
的实例,没有特殊行为.如果您有 type
以外的元类,则在查找类的属性时会应用完整的实例查找规则(只是该类的类是元类).
Everything else is pretty much the same, it's just that the book is glossing over the concept of metaclasses here, since most classes are instances of the base type
, which has no special behaviors. If you have a metaclass other than type
, the full instance lookup rules apply when looking up attributes on a class (it's just the class of the class is the metaclass).
他们可能在早期试图避免元类的复杂性,但在这个过程中,实例查找的规则似乎不适用于类;确实如此,只是类在基本查找过程中添加了一些额外的规则.
They were probably trying to avoid the complexity of metaclasses early on, but in the process made it seem like the rules for instance lookup didn't apply to classes; they do, it's just that classes add a couple extra rules to the basic lookup procedure.
这篇关于为什么从类和实例中获取属性的查找过程不同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:为什么从类和实例中获取属性的查找过程不同?
基础教程推荐
- Dask.array.套用_沿_轴:由于额外的元素([1]),使用dask.array的每一行作为另一个函数的输入失败 2022-01-01
- 筛选NumPy数组 2022-01-01
- 如何让 python 脚本监听来自另一个脚本的输入 2022-01-01
- 用于分类数据的跳跃记号标签 2022-01-01
- 何时使用 os.name、sys.platform 或 platform.system? 2022-01-01
- 使用PyInstaller后在Windows中打开可执行文件时出错 2022-01-01
- 如何在海运重新绘制中自定义标题和y标签 2022-01-01
- Python kivy 入口点 inflateRest2 无法定位 libpng16-16.dll 2022-01-01
- 在 Python 中,如果我在一个“with"中返回.块,文件还会关闭吗? 2022-01-01
- 线程时出现 msgbox 错误,GUI 块 2022-01-01