为啥从类和实例中获取属性的查找过程不同?

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 上,查找分两步进行:

    nameC.__dict__ 中的键时,C.nameC.__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 对象),但它会在每个类属性上发生元类不通过描述符提供覆盖。我会调整答案以涵盖这一点。

以上是关于为啥从类和实例中获取属性的查找过程不同?的主要内容,如果未能解决你的问题,请参考以下文章

什么类和对象

jQuery - 从类的元素中获取属性值的列表

为啥不在实例属性查找中搜索元类的属性?

朴素贝叶斯 (Weka) - 属性总数 x 实例总数 - 为啥不同?

10类和对象

10类和对象