当 Python __call__ 方法获得额外的第一个参数时?
When the Python __call__ method gets extra first argument?
下面的例子
import types
import pprint
class A:
def __call__(self, *args):
pprint.pprint('[A.__call__] self=%r, args=%r'
% (self, list(args)))
class B:
pass
if __name__ == '__main__':
a = A()
print(callable(a))
a(1, 2)
b = B()
b.meth = types.MethodType(a, b)
b.meth(3, 4)
打印
True
'[A.__call__] self=<__main__.A object at 0xb7233c2c>, args=[1, 2]'
('[A.__call__] self=<__main__.A object at 0xb7233c2c>, args=[<__main__.B '
'object at 0xb71687cc>, 3, 4]')
__call__ 方法参数的数量在
b.meth(3, 4) 示例。请解释第一个(__main__.B
object...) 以及 Python 何时提供它?
在 Debian 9.9 Stretch 上使用 Python 3.5.3
这里的重要概念是 class 函数是一个将 'self' 作为第一个参数绑定到它的函数。
我将在几个示例中进行演示。以下代码对于所有示例都是相同的:
import types
# Class with function
class A:
def func(*args):
print('A.func(%s)'%(', '.join([str(arg) for arg in args])))
# Callable function-style class
class A_callable:
def __call__(*args):
print('A_callable.__call__(%s)'%(', '.join([str(arg) for arg in args])))
# Empty class
class B():
pass
# Function without class
def func(*args):
print('func(%s)'%(', '.join([str(arg) for arg in args])))
现在让我们考虑几个例子:
>>> func(42)
func(42)
这一点很明显。它只是用参数 42
.
调用函数 func
接下来更有意思的是:
>>> A().func(42)
A.func(<__main__.A object at 0x7f1ed9ed2908>, 42)
>>> A_callable()(42)
A_callable.__call__(<__main__.A_callable object at 0x7f1ed9ed28d0>, 42)
您可以看到 class 对象 self
作为第一个参数自动提供给函数。需要注意的是,self
参数是不是添加的,因为该函数存储在一个对象中,而是因为该函数是构造的 作为对象的一部分,因此对象 绑定到它 .
演示:
>>> tmp = A().func
>>> tmp
<bound method A.func of <__main__.A object at 0x7f1ed9ed2978>>
>>> tmp(42)
A.func(<__main__.A object at 0x7f1ed9ed2978>, 42)
>>> tmp = A_callable().__call__
>>> tmp
<bound method A_callable.__call__ of <__main__.A_callable object at 0x7f1ed9ed2908>>
>>> tmp(42)
A_callable.__call__(<__main__.A_callable object at 0x7f1ed9ed2908>, 42)
self
参数 不会被添加 因为你在它之前写了 a.
。它是函数对象本身的一部分,将它存储在变量中仍然保持绑定。
您还可以手动将 class 对象绑定到函数,如下所示:
>>> tmp = types.MethodType(func, B)
>>> tmp
<bound method func of <class '__main__.B'>>
>>> tmp(42)
func(<class '__main__.B'>, 42)
另一方面,仅将函数分配给 class 不会 将 self
绑定到该函数。如前所述,参数 not 在调用时动态添加,但在构造时静态添加:
>>> b = B()
>>> b.func = func
>>> b.func
<function func at 0x7f1edb58fe18>
>>> b.func(42)
func(42) # does NOT contain the `self` argument
这就是为什么我们需要将 self
显式绑定到函数,如果我们想将它添加到对象:
>>> b = B()
>>> b.func = types.MethodType(func, b)
>>> b.func
<bound method func of <__main__.B object at 0x7f1ed9ed2908>>
>>> b.func(42)
func(<__main__.B object at 0x7f1ed9ed2908>, 42)
唯一剩下的就是了解 binding 的工作原理。如果一个方法 func
有一个参数 a
绑定到它,并且用 *args
调用,它将添加 a
到 beginning 的 *args
然后将其传递给函数。 开头在这里很重要。
现在我们知道理解您的代码所需的一切:
>>> a = A_callable()
>>> b = B()
>>> b.func = types.MethodType(a, b)
>>> b.func
<bound method ? of <__main__.B object at 0x7f1ed97e9fd0>>
>>> b.func(42)
A_callable.__call__(<__main__.A_callable object at 0x7f1ed97fb2b0>, <__main__.B object at 0x7f1ed97e9fd0>, 42)
首先,我们可以将 b.func
更改为普通 tmp
,因为如前所述,向对象添加函数不会更改其类型或功能。只有绑定 self
可以。
然后,让我们逐条分析代码:
>>> a = A_callable()
>>> b = B()
到目前为止一切顺利。我们有一个空对象 b
和一个可调用对象 a
.
>>> tmp = types.MethodType(a,b)
这一行是症结所在。明白了这一点,就明白了一切。
tmp
现在是绑定了 b
的函数 a
。这意味着,如果我们调用 tmp(42)
,它会将 b
添加到其参数的 beginning。 a
因此将收到 b, 42
。然后,因为 a
是可调用的,它会将其参数转发给 a.__call__
.
这意味着,我们处于 tmp(42)
等于 a.__call__(b, 42)
的位置。
因为__call__
是A_callable
的class函数,a
在[=30=的构造过程中自动绑定到__call__
函数].因此,在参数到达 A_callable.__call__
之前,a
被添加到参数列表的 beginning,这意味着参数现在是 a, b, 42
.
现在我们处于 tmp(42)
等于 A_callable.__call__(a, b, 42)
的位置。这正是您所看到的:
>>> tmp = types.MethodType(a, b)
>>> tmp(42)
A_callable.__call__(<__main__.A_callable object at 0x7f1ed97fb2b0>, <__main__.B object at 0x7f1ed97e9fd0>, 42)
>>> A_callable.__call__(a, b, 42)
A_callable.__call__(<__main__.A_callable object at 0x7f1ed97fb2b0>, <__main__.B object at 0x7f1ed97e9fd0>, 42)
现在,如果您将参数拆分为 self, *args
,您基本上只是拿走第一个参数并将其存储在 self
中。您的第一个参数是 a
,因此 self
将是 a
,而您的另一个 *args
将是 b, 42
。同样,这正是您所看到的。
下面的例子
import types
import pprint
class A:
def __call__(self, *args):
pprint.pprint('[A.__call__] self=%r, args=%r'
% (self, list(args)))
class B:
pass
if __name__ == '__main__':
a = A()
print(callable(a))
a(1, 2)
b = B()
b.meth = types.MethodType(a, b)
b.meth(3, 4)
打印
True
'[A.__call__] self=<__main__.A object at 0xb7233c2c>, args=[1, 2]'
('[A.__call__] self=<__main__.A object at 0xb7233c2c>, args=[<__main__.B '
'object at 0xb71687cc>, 3, 4]')
__call__ 方法参数的数量在 b.meth(3, 4) 示例。请解释第一个(__main__.B object...) 以及 Python 何时提供它?
在 Debian 9.9 Stretch 上使用 Python 3.5.3
这里的重要概念是 class 函数是一个将 'self' 作为第一个参数绑定到它的函数。
我将在几个示例中进行演示。以下代码对于所有示例都是相同的:
import types
# Class with function
class A:
def func(*args):
print('A.func(%s)'%(', '.join([str(arg) for arg in args])))
# Callable function-style class
class A_callable:
def __call__(*args):
print('A_callable.__call__(%s)'%(', '.join([str(arg) for arg in args])))
# Empty class
class B():
pass
# Function without class
def func(*args):
print('func(%s)'%(', '.join([str(arg) for arg in args])))
现在让我们考虑几个例子:
>>> func(42)
func(42)
这一点很明显。它只是用参数 42
.
func
接下来更有意思的是:
>>> A().func(42)
A.func(<__main__.A object at 0x7f1ed9ed2908>, 42)
>>> A_callable()(42)
A_callable.__call__(<__main__.A_callable object at 0x7f1ed9ed28d0>, 42)
您可以看到 class 对象 self
作为第一个参数自动提供给函数。需要注意的是,self
参数是不是添加的,因为该函数存储在一个对象中,而是因为该函数是构造的 作为对象的一部分,因此对象 绑定到它 .
演示:
>>> tmp = A().func
>>> tmp
<bound method A.func of <__main__.A object at 0x7f1ed9ed2978>>
>>> tmp(42)
A.func(<__main__.A object at 0x7f1ed9ed2978>, 42)
>>> tmp = A_callable().__call__
>>> tmp
<bound method A_callable.__call__ of <__main__.A_callable object at 0x7f1ed9ed2908>>
>>> tmp(42)
A_callable.__call__(<__main__.A_callable object at 0x7f1ed9ed2908>, 42)
self
参数 不会被添加 因为你在它之前写了 a.
。它是函数对象本身的一部分,将它存储在变量中仍然保持绑定。
您还可以手动将 class 对象绑定到函数,如下所示:
>>> tmp = types.MethodType(func, B)
>>> tmp
<bound method func of <class '__main__.B'>>
>>> tmp(42)
func(<class '__main__.B'>, 42)
另一方面,仅将函数分配给 class 不会 将 self
绑定到该函数。如前所述,参数 not 在调用时动态添加,但在构造时静态添加:
>>> b = B()
>>> b.func = func
>>> b.func
<function func at 0x7f1edb58fe18>
>>> b.func(42)
func(42) # does NOT contain the `self` argument
这就是为什么我们需要将 self
显式绑定到函数,如果我们想将它添加到对象:
>>> b = B()
>>> b.func = types.MethodType(func, b)
>>> b.func
<bound method func of <__main__.B object at 0x7f1ed9ed2908>>
>>> b.func(42)
func(<__main__.B object at 0x7f1ed9ed2908>, 42)
唯一剩下的就是了解 binding 的工作原理。如果一个方法 func
有一个参数 a
绑定到它,并且用 *args
调用,它将添加 a
到 beginning 的 *args
然后将其传递给函数。 开头在这里很重要。
现在我们知道理解您的代码所需的一切:
>>> a = A_callable()
>>> b = B()
>>> b.func = types.MethodType(a, b)
>>> b.func
<bound method ? of <__main__.B object at 0x7f1ed97e9fd0>>
>>> b.func(42)
A_callable.__call__(<__main__.A_callable object at 0x7f1ed97fb2b0>, <__main__.B object at 0x7f1ed97e9fd0>, 42)
首先,我们可以将 b.func
更改为普通 tmp
,因为如前所述,向对象添加函数不会更改其类型或功能。只有绑定 self
可以。
然后,让我们逐条分析代码:
>>> a = A_callable()
>>> b = B()
到目前为止一切顺利。我们有一个空对象 b
和一个可调用对象 a
.
>>> tmp = types.MethodType(a,b)
这一行是症结所在。明白了这一点,就明白了一切。
tmp
现在是绑定了 b
的函数 a
。这意味着,如果我们调用 tmp(42)
,它会将 b
添加到其参数的 beginning。 a
因此将收到 b, 42
。然后,因为 a
是可调用的,它会将其参数转发给 a.__call__
.
这意味着,我们处于 tmp(42)
等于 a.__call__(b, 42)
的位置。
因为__call__
是A_callable
的class函数,a
在[=30=的构造过程中自动绑定到__call__
函数].因此,在参数到达 A_callable.__call__
之前,a
被添加到参数列表的 beginning,这意味着参数现在是 a, b, 42
.
现在我们处于 tmp(42)
等于 A_callable.__call__(a, b, 42)
的位置。这正是您所看到的:
>>> tmp = types.MethodType(a, b)
>>> tmp(42)
A_callable.__call__(<__main__.A_callable object at 0x7f1ed97fb2b0>, <__main__.B object at 0x7f1ed97e9fd0>, 42)
>>> A_callable.__call__(a, b, 42)
A_callable.__call__(<__main__.A_callable object at 0x7f1ed97fb2b0>, <__main__.B object at 0x7f1ed97e9fd0>, 42)
现在,如果您将参数拆分为 self, *args
,您基本上只是拿走第一个参数并将其存储在 self
中。您的第一个参数是 a
,因此 self
将是 a
,而您的另一个 *args
将是 b, 42
。同样,这正是您所看到的。