Python 3.x 当Python调用方法获得额外的第一个参数时?

Python 3.x 当Python调用方法获得额外的第一个参数时?,python-3.x,python-datamodel,Python 3.x,Python Datamodel,下面的示例 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))

下面的示例

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]')
True
“[A.\u调用\u]self=,args=[1,2]”
(“[A.[U.[U]调用[U]self=,args=[,3,4]”)
在中更改了_调用_方法参数的数量 b、 meth(3,4)示例。请解释第一个(uuu main_uuuu.B 对象…)以及Python何时提供它


在Debian 9.9 Stretch上使用Python 3.5.3,这里的重要概念是类函数是一个“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)
现在我们来考虑几个例子:

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)
self
参数不会被添加,因为您在它前面写了
a.
。它是函数对象本身的一部分,将其存储在变量中仍然保持绑定

还可以手动将类对象绑定到函数,如下所示:

>>> tmp = types.MethodType(func, B)
>>> tmp
<bound method func of <class '__main__.B'>>
>>> tmp(42)
func(<class '__main__.B'>, 42)
这就是为什么如果要将函数添加到对象中,我们需要明确地将函数绑定到
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)
首先,我们可以将
b.func
更改为普通
tmp
,因为如前所述,向对象添加函数不会更改其类型或功能。只有绑定
self
可以

然后,让我们一步一步地浏览代码:

>>> a = A_callable()
>>> b = B()
到目前为止还不错。我们有一个空对象
b
和一个可调用对象
a

>>> tmp = types.MethodType(a,b)
这条线是关键。如果你明白这一点,你就会明白一切

tmp
现在是绑定了
b
的函数
a
。这意味着,如果我们调用
tmp(42)
,它会在参数的开头添加
b
<因此,代码>a将接收
b,42
。然后,因为
a
是可调用的,所以它将其参数转发给
a.。\uuuu call\uuuu

这意味着,我们正处在
tmp(42)
等于
a的点上

由于
\uuuuu call\uuuu
a\u callable
的一个类函数,
a
在构建
a
的过程中自动绑定到
\uuu call\uuuu
函数。因此,在参数到达
A\u可调用之前,
A
被添加到参数列表的开头,这意味着参数现在是
A,b,42

现在我们正处在
tmp(42)
等于
A\u可调用的点上。这正是你所看到的:

>>> 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)
>tmp=types.MethodType(a,b)
>>>tmp(42)
可调用的。调用(,42)
>>>可调用的。调用(A,b,42)
可调用的。调用(,42)

现在,如果您将参数拆分为
self、*args
,基本上只需去掉第一个参数并将其存储在
self
中即可。您的第一个参数是
a
,因此
self
将是
a
,而您的其他
*参数将是
b,42
。同样,这正是您所看到的。

这里的重要概念是,类函数是一个将“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)
现在我们来考虑几个例子:

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)
self
参数不会被添加,因为您在它前面写了
a.
。它是函数对象本身的一部分,将其存储在变量中仍然保持绑定

还可以手动将类对象绑定到函数,如下所示:

>>> tmp = types.MethodType(func, B)
>>> tmp
<bound method func of <class '__main__.B'>>
>>> tmp(42)
func(<class '__main__.B'>, 42)
这就是为什么如果要将函数添加到对象中,我们需要明确地将函数绑定到
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)
首先,我们可以将
b.func
更改为普通
tmp
,因为如前所述,向对象添加函数不会更改其类型或功能。只有绑定
self
可以

然后,让我们一步一步地浏览代码:

>>> a = A_callable()
>>> b = B()
到目前为止还不错。我们有一个空对象
b
和一个可调用对象
a

>>> tmp = types.MethodType(a,b)
这条线是关键。如果你明白这一点,你就会明白一切

tmp
现在是绑定了
b
的函数
a
。这意味着,如果我们调用
tmp(42)
,它会在参数的开头添加
b
<因此,代码>a
将接收
b,42
。然后,因为
a
是可调用的,所以它将其参数转发给
a.。\uuuu call\uuuu

这意味着,我们正处在
tmp(42)
等于
a的点上

由于
\uuuuu call\uuuu
a\u callable
的一个类函数,
a
在构建
a
的过程中自动绑定到
\uuu call\uuuu
函数。因此,在参数到达
A\u可调用之前,
A
被添加到参数列表的开头,这意味着参数现在是
A,b,42

现在我们正处在
tmp(42)
等于
A\u可调用的点上。这正是你所看到的:

>>> 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)
>tmp=types.MethodType(a,b)
>>>tmp(42)
可调用的。调用(,42)
>>>可调用的。调用(A,b,42)
可调用的。调用(,42)
现在,如果您将参数拆分为
self、*args
,基本上只需去掉第一个参数并将其存储在
self
中即可。您的第一个参数是
a
,因此
self
将是
a
,而您的其他
*参数将是
b,42
。同样,这正是你所看到的