Python - 作为 class 属性成为绑定方法的功能

Python - function as class attribute becomes a bound method

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

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)

Python 不是 基于消息的面向对象系统1。相反,类似于 JavaScript,属性被解析为 first-class 函数,然后被调用;正如所发现的那样,这种行为在机制上略有不同。

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

此外(也许是问题的重点),Python 在建立实例成员绑定时不区分使用 def f..f = some_func();可以说这符合 classes.

之外的行为

在示例中,将函数分配给实例'makes it expect to be treated like an instance method'。在这两种情况下调用的函数完全相同——无参数;只有这样的未来用法才相关。

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

a.f 返回绑定方法的行为 - 将自动将绑定对象作为 self 提供给第一个参数的函数 - 独立于函数源完成。在这种情况下,这意味着无参数函数在 'bound' 时不能使用,因为它不接受 self 参数。

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

g = a.f
g()

在这种情况下调用 g() 等同于调用 func(a).


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

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

实例方法

创建 class A 的实例(名为 a)会导致该实例具有属性 f,并且您可以通过名称 [=18] 访问它=].这是一个绑定方法(因为它绑定到对象 a)。

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

由于这个原因,错误说 func 没有参数(因为它被定义为 def func():)但是收到了 1 (self).

要执行您想要的操作,您应该告诉 python 您正在使用 static method

def func():
    pass

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

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

的一个属性
# 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