我经常看到这种情况:
def __get__(self, instance, owner=None):
为什么有些人None
对owner
参数使用默认值?
这甚至在Python 文档中完成:
descr.__get__(self, obj, type=None) --> value
我经常看到这种情况:
def __get__(self, instance, owner=None):
为什么有些人None
对owner
参数使用默认值?
这甚至在Python 文档中完成:
descr.__get__(self, obj, type=None) --> value
因为所有者可以很容易地从实例派生,所以第二个参数是可选的。只有当没有实例可以从中派生所有者时,才需要所有者参数。
这在引入描述符的提案PEP 252 -使类型看起来更像类中有所描述:
__get__
: 一个可使用一个或两个参数调用的函数,用于从对象中检索属性值。这也称为“绑定”操作,因为在方法描述符的情况下它可能返回“绑定方法”对象。第一个参数X
是必须从中检索或绑定属性的对象。 当X
是None
时,可选的第二个参数 ,T
应该是元对象,并且绑定操作可能会返回限制为 的实例的未绑定T
方法。
(粗体强调我的)。
从第一天开始,绑定就意味着只适用于实例,类型是可选的。例如,方法不需要它,因为它们可以单独绑定到实例:
>>> class Foo: pass
...
>>> def bar(self): return self
...
>>> foo = Foo()
>>> foo.bar = bar.__get__(foo) # look ma! no class!
>>> foo.bar
<bound method Foo.bar of <__main__.Foo object at 0x10a0c2710>>
>>> foo.bar()
<__main__.Foo object at 0x10a0c2710>
此外,第二个参数可以很容易地从第一个参数推导出来;见证 aclassmethod
仍然对班级具有约束力,即使我们没有通过:
>>> classmethod(bar).__get__(foo)
<bound method type.bar of <class '__main__.Foo'>>
>>> classmethod(bar).__get__(foo)()
<class '__main__.Foo'>
该参数首先存在的唯一原因是支持绑定到类,例如,当没有要绑定的实例时。又是类方法;绑定到None
作为实例不起作用,它只有在我们实际传入类时才起作用:
>>> classmethod(bar).__get__(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __get__(None, None) is invalid
>>> classmethod(bar).__get__(None, Foo)
<bound method type.bar of <class '__main__.Foo'>>
这是执行此操作的标准方法;我见过的所有 Python 内置描述符都这样做,包括函数、属性、静态方法等。我知道在描述符协议中没有任何情况__get__
会在没有所有者参数的情况下被调用,但如果你想__get__
手动调用,它可以不必通过所有者很有用。所有者的论点通常没有多大作用。
例如,您可能需要一种更简洁的方法来为单个对象提供新方法。以下装饰器清理语法并让方法可以访问self
:
def method_of(instance):
def method_adder(function):
setattr(instance, function.__name__, function.__get__(instance))
return function
return method_adder
@method_of(a)
def foo(self, arg1, arg2):
stuff()
现在a
有foo
方法了。我们手动使用函数的__get__
方法foo
来创建一个绑定的方法对象,就像其他任何方法一样,除了因为这个方法没有与类关联,我们没有传递__get__
一个类。几乎唯一的区别是,当您打印方法对象时,您看到?.foo
的是SomeClassName.foo
.
因为这就是指定描述符协议的方式:
descr.__get__(self, obj, type=None) --> value
cf https://docs.python.org/2/howto/descriptor.html#descriptor-protocol
当在类而不是实例上查找描述符时,该type
参数允许访问在其上查找描述符的类。由于您可以从实例中获取类,因此在实例上查找描述符时它在某种程度上是多余的,因此它已成为可选的以允许较少冗长的desc.__get__(obj)
调用(而不是desc.__get__(obj, type(obj))
)。