关于Python描述符和<descriptor howto =“”guide =“”> </descriptor>的混淆

时间:2013-04-19 06:18:58

标签: python descriptor

最近我读了official HOW-TO about Python descriptors,它实际上源自Raymond Hettinger很久以前写的an essay。但在阅读了几次之后,我仍然对它的某些部分感到困惑。我会引用一些段落,然后是我的困惑和问题。


  

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

  • 这和班级一样吗?如果类的字典有一个与数据/非数据描述符同名的条目,那么它的优先级是什么?

  

对于对象,机器位于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))仍然会继续?返回此类或实例的 dict 中的属性直接更简单?
    • 如果实例的字典有一个与非数据描述符同名的条目,根据第一个引号中的优先级规则,字典条目优先,此时转换是否仍然继续?或者只返回 dict 中的值?

  

非数据描述符提供了一种简单的机制,可以将通常的绑定函数模式转换为方法。

  • 是否选择了非数据描述符,因为函数/方法只能得到,但不能设置
  • 将函数绑定到方法的基本机制是什么?由于类字典将方法存储为函数,如果我们分别使用类及其实例调用相同的方法,底层函数如何判断其第一个参数是否应该 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 个答案:

答案 0 :(得分:2)

  

这和班级一样吗?如果类的字典具有与数据/非数据描述符同名的条目,那么它的优先级是什么?

不,如果在超类和子类中都定义了属性,则完全忽略超类值。

  

如果类或实例的属性不是描述符,那么转换(即将b.x转换为type(b).__dict__['x'].__get__(b, type(b))而将B.x转换为B.__dict__['x'].__get__(None, B))是否仍会继续?

不,它直接返回从类__dict__获得的对象。或者等效地,是的,如果你假装所有对象默认都有一个忽略其参数并返回__get__()的方法self

  

如果实例的字典有一个与非数据描述符同名的条目,根据第一个引用中的优先级规则,字典条目优先,此时转换是否还会继续?或者只返回其字典中的值?

您引用的段落中可能没有明确的内容(可能是在其他地方写下来的)是b.x决定返回b.__dict__['x']时,在任何情况下都不会调用__get__。语法__get__b.x决定返回位于 dict中的对象时,会调用B.x

  

是否选择了非数据描述符,因为只能获取函数/方法,但无法设置?

是的:它们是Python中“旧式”类模型的概括,即使B.f = 42是生活在B类中的函数对象,也可以说f。使用不相关的对象覆盖函数对象。另一方面,数据描述符具有不同的逻辑,以支持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__的方式。