【问题标题】:Why does __get__ take an owner while __set__ and __delete__ do not?为什么 __get__ 获取所有者而 __set__ 和 __delete__ 不获取?
【发布时间】:2017-12-09 19:20:54
【问题描述】:

来自 Python data model documentation

object.__get__(self, instance, owner=None)

调用以获取所有者类的属性(类属性访问)或该类的实例(实例属性访问)。可选的owner 参数是所有者类,而instance 是通过owner 访问属性时访问属性的实例或None

此方法应返回计算的属性值或引发AttributeError 异常。

PEP 252 指定__get__() 可以用一个或两个参数调用。 Python 自己的内置描述符支持此规范;但是,某些第三方工具可能具有需要两个参数的描述符。 Python 自己的 __getattribute__() 实现总是传入两个参数,无论它们是否需要。

object.__set__(self, instance, value)

调用以将所有者类的实例 instance 上的属性设置为新值 value。

注意,添加__set__()__delete__() 会将描述符的种类更改为“数据描述符”。有关详细信息,请参阅调用描述符。

object.__delete__(self, instance)

调用以删除所有者类的实例instance 上的属性。

为什么__get__ 使用owner__set____delete__ 没有?

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

  • 无论是属于所有者类的实例还是属于所有者类,我们都可以获得一个属性,
  • 我们可以设置和删除属于所有者类实例的属性,但不能在属于所有者类时设置和删除?

我的问题实际上是this one 的一部分。

【问题讨论】:

    标签: python attributes datamodel python-descriptors


    【解决方案1】:

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

    __set__不适用于设置类本身的属性,所以owner没有用。

    【讨论】:

    • 谢谢。我的问题实际上是stackoverflow.com/questions/44937897/… 的一部分
    • 谢谢。为什么“__set__不适用于设置类本身的属性”,而__get__适用于获取类或类实例的属性?
    • @Tim:这就是语言的设计方式。他们可能没有足够的用例来使__set__ 应用于课程。如果您想了解实际的基本原理,则必须搜索 Python 邮件列表。
    【解决方案2】:

    让我们考虑对原始对象的属性访问,它最终在所属类上找到描述符并调用其__get____set__或@987654329 @方法。

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

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

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

    但是 Guido van Rossum 对描述符 API 采用了不同但等效的设计:

    • instance 对象参数,用于在它是所属类的实例时提供源对象(对于 __get____set____delete__),以及源对象是实例还是子类通过使用 None 为后者(因此仅适用于 __get__);
    • owner 类型参数,用于在其是所属类的子类时提供原始对象(因此仅适用于 __get__);
    • value 对象参数,用于提供属性值(因此仅适用于__set__)。

    他在PEP 252: Making Types Look More Like Classes 中详细说明了描述符 API 的设计,发表于 2001 年 4 月 19 日:

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

    属性描述符可能具有以下属性。在示例中,x 是对象,Cx.__class__x.meth() 是方法,x.ivar 是数据属性或实例变量。所有属性都是可选的——一个特定的属性可能出现在给定的描述符上,也可能不出现。属性不存在表示对应的信息不可用或对应的功能没有实现。

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-15
      • 2020-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-17
      相关资源
      最近更新 更多