为啥从类和实例中获取属性的查找过程不同?
Posted
技术标签:
【中文标题】为啥从类和实例中获取属性的查找过程不同?【英文标题】:Why are the lookup procedures for getting an attribute from a class and from an instance different?为什么从类和实例中获取属性的查找过程不同? 【发布时间】:2017-12-09 19:59:14 【问题描述】:Python in a Nutshell 描述了获取属性时的查找过程。本书区分了两种情况
从类中获取属性时的查找过程,例如cls.name
从类中获取属性
当您使用语法
C.name
来引用属性时 在类对象C
上,查找分两步进行:
当
name
是C.__dict__
中的键时,C.name
从C.__dict__['name']
获取值v
。然后,当v
是 一个描述符(即type(v)
提供一个名为__get__
),C.name
的值是调用的结果type(v).__get__(v, None, C)
。当v
不是描述符时,C.name is v
的值。当
name
不是C.__dict__
中的键时,C.name
将查找委托给C
的基类,这意味着它在C
的基类上循环 祖先类并尝试对每个类进行名称查找(在方法中 解决顺序,如页面上的“方法解决顺序”中所述 113).
the lookup procedure when getting an attribute from an instance, e.g. obj.name
由于在 Python 3 中,每个类对象实际上都是其元类的一个实例(例如type
类),根据本书,为什么从类中获取属性的查找过程和获取属性的查找过程是来自不同实例的属性?
【问题讨论】:
【参考方案1】:它们并没有非常不同,书中的描述涵盖了它们不同的两种方式:
-
在类实例上找到的描述符(在类上找不到之后)不会被调用(
a.x = somedescriptor()
其中a
是类实例,而不是类,后面跟a.x
只会返回实例somedescriptor()
你刚刚创建的),而在元类实例上找到的描述符,即一个类(在元类上找不到之后)被调用,None
作为它被调用的实例(A.x = somedescriptor()
其中A
是元类实例,而不是元类,将在您刚刚创建的 somedescriptor()
上调用 .__get__(None, A)
)。这允许像 @classmethod
这样的东西通过将方法绑定到类本身来工作,无论它是在类的实例还是类本身上查找。
类实例没有“父实例”的概念(类实例本身的命名空间是一个平面dict
,即使与该类实例关联的属性是由多个继承级别的方法定义的) ,因此基于 MRO 的查找的想法是元类实例所独有的。
其他一切都差不多,只是本书在这里掩盖了元类的概念,因为大多数类都是基类type
的实例,它没有特殊行为。如果您有type
以外的元类,则在查找类的属性时会应用完整的实例查找规则(只是该类的类是元类)。
他们可能在早期试图避免元类的复杂性,但在这个过程中,实例查找的规则似乎不适用于类;确实如此,只是类在基本查找过程中添加了一些额外的规则。
【讨论】:
谢谢!你的意思是从书中的类中获取属性的查找过程只有在类是type
类的实例时才有效?
请问您对setting attribute的看法?
@Tim:这意味着当它是type
的一个实例时,规则按照所写的方式应用,但是如果它有一些其他元类,则规则可以调用所有的行为“正常”实例。一个非常简单的元类最终可能表现得与type
相同(因为它是type
的子类,所以它根本不需要不同),一个更复杂的元类可能会定义所有使实例更复杂的古怪事物。
“它是type
的子类”,你的意思是“它是type
的一个实例”吗(我猜“它”的意思是“一个非常简单的元类”)?如果一个非常简单的元类与type
完全没有区别,那么使用非常简单的元类而不是直接使用type
作为元类的原因是什么?
@Maggyero:啊,你是对的。我花了一点时间弄清楚你在说什么(我最初将其误读为关于在使用元类访问内容时未在元类上调用描述符协议的声明)。您不能让常规实例在每个实例属性上调用描述符协议(说a.x = property(somefunc)
不会让a.x
做任何事情,只会返回原始property
对象),但它会在每个类属性上发生元类不通过描述符提供覆盖。我会调整答案以涵盖这一点。以上是关于为啥从类和实例中获取属性的查找过程不同?的主要内容,如果未能解决你的问题,请参考以下文章