为什么`object .__ get__`对所有者类有一个参数`owner`,而`object .__ set__`没有?

时间:2017-07-06 00:06:31

标签: python python-3.x

来自https://docs.python.org/3/reference/datamodel.html#implementing-descriptors

object.__get__(self, instance, owner)
     

调用以获取所有者类的属性(类属性   访问)或该类的实例(实例属性访问)。   owner始终是所有者类,而instance是实例   属性是通过属性访问的,或者属性是无   通过所有者访问。这个方法应该返回(计算)   属性值或引发AttributeError异常。

object.__set__(self, instance, value)
     

调用以在所有者类的实例实例上设置属性   一个新的价值,值。

为什么object.__get__对所有者类有参数owner,而object.__set__没有?

这是否意味着当描述符类同时提供__get____set__时,

  • 我们可以获取描述符属性,无论该属性是所有者类的属性还是该类的实例的属性,

  • 当属性是所有者类实例的属性时,我们可以设置描述符属性,但是当属性是所有者类的属性时,我们可以设置描述符属性吗?

我的问题实际上是What is the lookup procedure when setting an attribute from a class or from an instance?

的一部分

感谢。

2 个答案:

答案 0 :(得分:2)

owner主要用于获取类本身的属性,而不是实例。当您在实例上检索属性时,owner参数是多余的,因为它只是type(instance)

__set__并不适用于在类本身上设置属性,因此owner没有用。

答案 1 :(得分:0)

让我们考虑对 原始对象 的属性访问,它最终在 拥有类 上找到一个描述符并调用它的 __get__、{{1} } 或 __set__ 方法。

  • 对于__delete__,关于原始对象是拥有类的实例还是子类的信息是必要的,因为classmethod添加拥有类的子类为在转发调用之前的第一个参数,并且因为 Python 2 unbound methods 在转发调用之前检查第一个参数是否是拥有类的实例(第二个原因现在只是历史,因为 Python 3 用普通函数替换了未绑定的方法) .因此,__get__ 需要两条信息:要从中检索属性值的源对象,以及源对象是所属类的实例还是子类。
  • 对于 __get____set__,关于原始对象是所属类的实例还是子类的信息不是必需的,因为这些方法仅在原始对象是拥有类的实例,因为如果在原始对象是拥有类的子类时也调用它们,则不可能更改错误的描述符,因为类字典是只读的 types.MappingProxyType。所以 __delete__ 需要两条信息:设置属性值的原始对象和属性值。而 __set__ 需要一条信息:要从中删除属性值的原始对象。

提供此信息的一种直接方法是使用以下参数设计描述符 API:

  • 一个 __delete__ 对象参数,用于提供原始对象(origin__get____set__ 也是如此);
  • 一个 __delete__ 布尔参数,用于提供原始对象是所属类的实例还是子类(因此仅适用于 isinstance);
  • 用于提供属性值的 __get__ 对象参数(因此仅适用于 value)。

然而,Guido van Rossum 采用了一种不同但等效的描述符 API 设计:

  • 一个 __set__ 对象参数,用于在原始对象是拥有类的实例时提供原始对象(对于 instance__get____set__),以及是否原始对象是拥有类的实例或子类,对后者使用 __delete__(因此仅用于 None);
  • 一个 __get__ 类型参数,用于在原始对象是拥有类的子类时提供原始对象(因此仅适用于 owner);
  • 用于提供属性值的 __get__ 对象参数(因此仅适用于 value)。

他在 2001 年 4 月 19 日发布的 PEP 252: Making Types Look More Like Classes 中指定了描述符 API 的设计:

<块引用>

属性描述符 API 属性描述符的规范

属性描述符可能具有以下属性。在示例中,__set__ 是一个对象,xCx.__class__ 是一个方法,而 x.meth() 是一个数据属性或实例变量。所有属性都是可选的——特定属性可能存在也可能不存在于给定的描述符中。不存在的属性意味着对应的信息不可用或对应的功能没有实现。

  • x.ivar:属性名称。由于别名和重命名,属性可能(附加地或唯一地)以不同的名称为人所知,但这是它的诞生名称。示例:__name__
  • C.meth.__name__ == 'meth':属性的文档字符串。这可能是 __doc__
  • None:声明该属性的类。描述符仅适用于作为此类的实例的对象(这包括其子类的实例)。示例:__objclass__
  • C.meth.__objclass__ is C:一个可调用的函数,带有一个或两个参数,用于从对象中检索属性值。这也称为“绑定”操作,因为在方法描述符的情况下它可能返回“绑定方法”对象。第一个参数 __get__() 是必须从中检索属性或必须将其绑定到的对象。当 XX 时,可选的第二个参数 None 应该是元对象,并且绑定操作可能会返回一个仅限于 T 实例的未绑定方法。当同时指定 TX 时,T 应该是 X 的实例。绑定操作返回的究竟是什么取决于描述符的语义;例如,静态方法和类方法(见下文)会忽略实例并绑定到类型。
  • T:一个带有两个参数的函数,用于设置对象的属性值。如果属性是只读的,则此方法可能会引发 __set__()TypeError 异常(两者都是允许的,因为历史上都发现了未定义或不可设置的属性)。示例:AttributeError ~~ C.ivar.set(x, y)

感谢 Martijn Pieters 在此答案中使用的论点(参见我们在 this post 评论中的讨论)。