为什么人们在 __get__ 中默认 owner 参数为 None?

Why do people default owner parameter to None in __get__?

我经常看到这个:

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

为什么有些人对 owner 参数使用默认值 None

这甚至在 Python docs:

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

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

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

cf https://docs.python.org/2/howto/descriptor.html#descriptor-protocol

type 参数允许访问 class,当在 class 而不是实例上查找描述符时,在该 class 上查找描述符。由于您可以从实例中获取 class,因此当在实例上查找描述符时,它在某种程度上是多余的,因此它已成为可选的,以允许不那么冗长的 desc.__get__(obj) 调用(而不是 desc.__get__(obj, type(obj))).

这是标准的做法;我见过的所有 Python 内置描述符都这样做,包括函数、属性、静态方法等。我知道在描述符​​协议中没有任何情况会在没有所有者参数的情况下调用 __get__,但是如果您想手动调用 __get__,则不必传递所有者可能会很有用。 owner 参数通常作用不大。

例如,you might want 一种为单个对象提供新方法的更简洁的方法。下面的装饰器清理了语法并让方法可以访问 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 方法。我们手动使用 foo 函数的 __get__ 方法来像其他方法一样创建绑定方法对象,除了因为这个方法没有与 class 关联,我们没有通过__get__一个class。几乎唯一的区别是,当您打印方法对象时,您看到的是 ?.foo 而不是 SomeClassName.foo.

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

这在引入描述符的提案中有描述,PEP 252 - Making Types Look More Like Classes:

__get__: a function callable with one or two arguments that retrieves the attribute value from an object. This is also referred to as a "binding" operation, because it may return a "bound method" object in the case of method descriptors. The first argument, X, is the object from which the attribute must be retrieved or to which it must be bound. When X is None, the optional second argument, T, should be meta-object and the binding operation may return an unbound method restricted to instances of 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>

此外,第二个参数很容易从第一个参数派生;见证 classmethod 仍然绑定到 class,即使我们没有传递一个:

>>> classmethod(bar).__get__(foo)
<bound method type.bar of <class '__main__.Foo'>>
>>> classmethod(bar).__get__(foo)()
<class '__main__.Foo'>

参数首先存在的唯一原因是支持绑定到 class,例如当没有要绑定的实例时。再次使用 class 方法;绑定到 None 作为实例将不起作用,只有当我们实际传入 class:

时它才会起作用
>>> 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'>>