Python:函数作为类属性成为一个绑定方法

我注意到,如果我定义一个类属性等于一个函数,那么当我创建该类的实例时,这个属性就变成了一个绑定方法。谁能给我解释一下这种行为的原因?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
In [9]: def func():
   ...:     pass
   ...:

In [10]: class A(object):
   ....:     f = func
   ....:    

In [11]: a = A()

In [12]: a.f
Out[12]: <bound method A.func of <__main__.A object at 0x104add190>>

In [13]: a.f()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-19134f1ad9a8> in <module>()
----> 1 a.f()
    global a.f = <bound method A.func of <__main__.A object at 0x104add190>>

TypeError: func() takes no arguments (1 given)


您为属性f指定了一个函数。因为函数现在在一个类中,它变成了一个方法,特别是一个绑定方法(因为它绑定到对象,这里有进一步的解释)。

类中的方法接收至少一个参数(self),除非特别告诉它不要——参见类方法和静态方法——否则,由于这个原因,错误显示func不接受参数(因为它被定义为def func():),而是接收了1 (self)

要做您想做的事情,您应该告诉python您正在使用一个静态方法

1
2
3
4
5
def func():
    pass

class A(object):
    f = staticmethod(func)

Python不是一个基于消息的OO系统1。相反,与JavaScript类似,属性被解析为一级函数,然后调用;这种行为在发现的这类机制中略有不同。

在Python中,要求方法至少有一个参数,通常称为self,当它作为方法调用时,将自动提供关联的实例。

此外(也许就问题的重点而言),Python在建立实例成员绑定时没有区分使用def f..f = some_func();可以说,这与类之外的行为相匹配。

在本例中,将函数赋值给实例"使它期望被当作实例方法对待"。在这两种情况下,它是完全相同的——无参数的——函数;只有将来才会用到它。

现在,与JavaScript不同,Python通过绑定方法的概念处理方法和对象关联——函数解析为方法总是"绑定"的。

a.f返回绑定方法的行为——函数将自动将绑定对象作为self提供给第一个参数——独立于函数的源。在本例中,这意味着在"绑定"时不能使用无参数函数,因为它不接受self参数。

作为演示,下面的方法也会以同样的方式失败,因为源底层方法不满足接受实例作为参数的最低要求:

1
2
g = a.f
g()

在这种情况下,调用g()等价于调用func(a)

为了便于比较,Java、c#、Ruby和SmallTalk都是基于消息的OO系统——在这些系统中,对象被告知使用"name"调用方法,而不是将方法(或函数)解析为可调用的值。