来自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?
的一部分感谢。
答案 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 的设计:
<块引用>属性描述符可能具有以下属性。在示例中,__set__
是一个对象,x
是 C
,x.__class__
是一个方法,而 x.meth()
是一个数据属性或实例变量。所有属性都是可选的——特定属性可能存在也可能不存在于给定的描述符中。不存在的属性意味着对应的信息不可用或对应的功能没有实现。
x.ivar
:属性名称。由于别名和重命名,属性可能(附加地或唯一地)以不同的名称为人所知,但这是它的诞生名称。示例:__name__
。C.meth.__name__ == 'meth'
:属性的文档字符串。这可能是 __doc__
。None
:声明该属性的类。描述符仅适用于作为此类的实例的对象(这包括其子类的实例)。示例:__objclass__
。C.meth.__objclass__ is C
:一个可调用的函数,带有一个或两个参数,用于从对象中检索属性值。这也称为“绑定”操作,因为在方法描述符的情况下它可能返回“绑定方法”对象。第一个参数 __get__()
是必须从中检索属性或必须将其绑定到的对象。当 X
为 X
时,可选的第二个参数 None
应该是元对象,并且绑定操作可能会返回一个仅限于 T
实例的未绑定方法。当同时指定 T
和 X
时,T
应该是 X
的实例。绑定操作返回的究竟是什么取决于描述符的语义;例如,静态方法和类方法(见下文)会忽略实例并绑定到类型。T
:一个带有两个参数的函数,用于设置对象的属性值。如果属性是只读的,则此方法可能会引发 __set__()
或 TypeError
异常(两者都是允许的,因为历史上都发现了未定义或不可设置的属性)。示例:AttributeError
~~ C.ivar.set(x, y)
。感谢 Martijn Pieters 在此答案中使用的论点(参见我们在 this post 评论中的讨论)。