关于Python描述符的困惑和<描述符指南>

7

最近我阅读了关于Python描述符的官方 HOW-TO,实际上这篇文章来源于Raymond Hettinger很久以前写的一篇文章。但是,阅读了几遍之后,我仍然对其中的一些部分感到困惑。我将引用一些段落,接着是我的疑惑和问题。


如果实例的字典中有与数据描述符同名的条目,则数据描述符优先。如果实例的字典中有与非数据描述符同名的条目,则字典条目优先。

  • 如果类的字典中有与数据/非数据描述符同名的条目,它们的优先级是什么?


对于对象,机制在object.__getattribute__()中,它将b.x转换为type(b).__dict__['x'].__get__(b, type(b))。该实现通过优先级链工作,使数据描述符优先于实例变量,实例变量优先于非数据描述符,并为提供的__getattr__()分配最低优先级。
对于类,机制在type.__getattribute__()中,它将B.x转换为B.__dict__['x'].__get__(None, B)
上面的两段文字讲述了描述符在属性访问时自动调用的过程。它列出了实例 (b.x) 和类 (B.x) 访问属性的区别。然而,这里有我的疑惑:
  • 如果一个类或实例的属性不是一个描述符,转换 (即将 b.x 转换为 type(b).__dict__['x'].__get__(b, type(b))B.x 转换为 B.__dict__['x'].__get__(None, B)) 是否仍会进行?直接从这个类或实例的字典中返回属性是否更简单?
  • 如果一个实例的字典中有与非数据描述符同名的条目,根据第一条引用中的优先规则,字典条目优先,此时转换是否仍会进行?还是只返回其字典中的值?

非数据描述符提供了一种简单的机制,用于变化通常将函数绑定到方法的模式。

  • 为什么选择非数据描述符?因为函数/方法只能获取,而不能设置吗?
  • 将函数绑定到方法的基本机制是什么?由于类字典将方法存储为函数,如果我们使用类和其实例分别调用相同的方法,底层函数如何知道其第一个参数是否应为self

函数具有 __get__() 方法,因此当作为属性访问时,它们可以转换为方法。非数据描述符将 obj.f(*args) 调用转换为 f(obj, *args)。调用 klass.f(*args) 变成了 f(*args)

  • 非数据描述符如何将 obj.f(*args) 调用转换为 f(obj, *args)
  • 非数据描述符如何将 klass.f(*args) 调用转换为 f(*args)
  • 上述两种转换的基本机制是什么?为什么类和实例之间存在差异?
  • __get__() 方法在上述情况下扮演什么角色?
1个回答

3
如果一个类的字典中有与数据/非数据描述符同名的条目,那么类的先例链是什么?否,如果一个属性在超类和子类中都定义了,超类的值将完全被忽略。
如果类或实例的属性不是描述符,则转换(即将b.x转换为type(b).__dict__ ['x'] .__get__(b,type(b))和B.x转换为B.__dict__ ['x'] .__get__(None,B))依然进行吗?不,它直接返回从类的__dict__获取的对象。或者等效地说,是的,如果你假装所有对象默认都有一个忽略其参数并返回self的方法__get__()。
如果实例的字典中存在与非数据描述符相同名称的条目,则根据第一段引文中的优先规则,字典条目具有优先权,此时是否仍会进行转换?还是只返回其dict中的值?在您引用的段落中不清楚(可能已经在其他地方写下),当b.x决定返回b.__dict__ ['x']时,无论如何都不会调用__get__。 __get__确切地在语法b.x或B.x决定返回位于类dict中的对象时调用。
非数据描述符之所以被选择,是因为函数/方法只能被获取,但不能被设置,是Python中旧式类模型的概括。在旧式类模型中,即使f是一个存储在B类中的函数对象,也可以说B.f = 42,这样函数对象就可以被不相关的对象覆盖。数据描述符则具有不同的逻辑,以支持property。
将函数绑定为方法的基础机制是什么?由于类字典将方法存储为函数,如果我们使用类及其实例分别调用相同的方法,底层函数如何知道它的第一个参数是否应该是self?
为了理解这个,您需要对 "方法对象" 有所了解。 语法 b.f(*args) 等同于 (b.f)(*args),这是两个步骤。第一步调用 f.__get__(b);这将返回一个存储 b 和 f 的方法对象。第二步调用该方法对象,该方法对象将通过添加 b 作为额外参数来调用原始的 f。这是在 B.f 中不会发生的事情,只是因为 B.f,即 f.__get__(None, B),只是 f(在 Python 3 中)。这是函数对象上设计特殊方法 __get__ 的方式。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接