15

我经常看到这种情况:

def __get__(self, instance, owner=None):

为什么有些人Noneowner参数使用默认值?

这甚至在Python 文档中完成:

descr.__get__(self, obj, type=None) --> value
4

3 回答 3

9

因为所有者可以很容易地从实例派生,所以第二个参数是可选的。只有当没有实例可以从中派生所有者时,才需要所有者参数。

这在引入描述符的提案PEP 252 -使类型看起来更像类中有所描述:

__get__: 一个可使用一个或两个参数调用的函数,用于从对象中检索属性值。这也称为“绑定”操作,因为在方法描述符的情况下它可能返回“绑定方法”对象。第一个参数X是必须从中检索或绑定属性的对象。 XNone时,可选的第二个参数 ,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'>>
于 2015-08-02T00:21:52.783 回答
5

这是执行此操作的标准方法;我见过的所有 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()

现在afoo方法了。我们手动使用函数的__get__方法foo来创建一个绑定的方法对象,就像其他任何方法一样,除了因为这个方法没有与类关联,我们没有传递__get__一个类。几乎唯一的区别是,当您打印方法对象时,您看到?.foo的是SomeClassName.foo.

于 2015-08-01T08:52:15.350 回答
2

因为这就是指定描述符协议的方式:

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)))。

于 2015-07-25T13:22:28.497 回答