22

我注意到,如果我在创建该类的实例时定义了一个等于函数的类属性,则该属性将成为绑定方法。有人可以解释一下这种行为的原因吗?

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

3 回答 3

25

A.f您为属性(f类的属性)分配了一个函数A。该属性A.f被定义为类的一部分。它是一个函数,因此默认情况下它是该类的实例方法。

创建a类的实例(命名)A会导致该实例具有属性f,并且您可以通过 name 访问它a.f。这是一个绑定方法(因为它绑定到对象a这里有进一步的解释)。

每个实例方法在被调用时都会自动接收实例作为其第一个参数(通常命名为self)。其他类型的方法是可能的: - 参见类方法和静态方法

出于这个原因,错误说不func接受任何参数(因为它被定义为def func():)但收到了 1(self)。

做你想做的事,你应该告诉python你正在使用静态方法

def func():
    pass

class A(object):
    f = staticmethod(func)
于 2016-02-10T18:01:03.120 回答
2

Python不是基于消息的 OO 系统1。相反,类似于 JavaScript,属性被解析为一等函数,然后被调用;正如发现的那样,这种行为的机制有所不同。

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

此外(也许是问题的关键),Python 没有区分使用def f..f = some_func()建立实例成员绑定。可以说这与类外的行为相匹配。

在示例中,将函数分配给实例“使其期望被视为实例方法”。在这两种情况下调用的函数完全相同 - 无参数 - 函数;只有此类的未来使用是相关的。

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

返回绑定方法的行为a.f- 将自动将绑定对象提供给第一个参数self的函数 - 与函数的源无关。在这种情况下,这意味着无参数函数在“绑定”时无法使用,因为它不接受self参数。

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

g = a.f
g()

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


1作为比较,Java、C#、Ruby 和 SmallTalk 是基于消息的 OO 系统——在这些系统中,对象被告知通过“名称”调用方法,而不是将方法(或函数)解析为可以调用的值.

于 2016-02-10T17:24:20.513 回答
0

派对有点晚了,但另一个可行的解决方案是将函数存储为字典,这是类的一个属性。

# Some arbitrary function
def f(x):
  return x

# A class, which will wrap the function as a dictionary object, which is a class attr
class Test:
  def __init__(self,f):
    self.f = {'f':f}
  
  def func(self,x):
    return self.f['f'](x)

# Now let's test the Test object
t = Test(f=f)
t.func(3)

>>>
3

这种方法比公认的答案更冗长,但是,它不涉及静态方法、装饰器或其他高级主题。因此,如果您被另一种首选方法吓倒,这将在紧要关头起作用。

编辑:如果您希望它足够强大以处理关键字参数:

class Test:
  def __init__(self,f):
    self.f = {'f':f}
  
  def func(self,p):
    return self.f['f'](**p)

def f(**p):
  return p['x'] * p['y']

t = Test(f=f)
t.func({'x':2,'y':3})

>>>
6
于 2021-05-03T17:36:13.983 回答