31

请看简单的例子:

class A:
  def __init__(self, flag):
    self.flag = flag

  def func(self):
    print self.flag

a = A(1)
b = A(2)
callback_a = a.func
callback_b = b.func

callback_a()
callback_b()

结果是:

1
2

它按预期运行。但我有一个问题。在 C 中,回调函数作为指针传递。在 Python 中,它应该有类似的方法来执行此操作,因此调用者知道函数的地址。但是在我的例子中,不仅传递了函数指针,还传递了参数(self),因为同一个类的同一个方法打印出不同的结果。所以我的问题是:

  1. Python中的这种方法是否在内存中只有一个副本?我的意思是任何方法的代码都只有一个副本,在我的示例中,该方法本身不会被克隆。我认为它应该只有一个副本,但在这里我仍然提出这个问题以获得更多输入。

  2. 我记得 Python 中的一切都是对象。那么在我的示例中,是否有两个具有不同参数但只有一份代码副本的函数实例?

4

2 回答 2

24

在 Python 中,回调不仅仅是对成员函数的引用。相反,它被“绑定”到它在创建时引用的对象。因此a.func创建了一个绑定到的可调用对象a,并b.func创建了一个绑定到 的可调用对象b

Python 只需要一种func()在内存中的实现,但它可能会在运行时创建一个或多个“蹦床”函数来完成绑定(我不确定这方面的内部细节,无论如何 Python 实现之间会有所不同)。

如果你打印id(callback_a)id(callback_b)你会得到不同的结果,表明它们确实是不同的可调用对象。

于 2012-04-04T01:34:33.413 回答
23

特定于 CPython,函数对象只有一份副本。在实例创建期间,该类将其命名空间中的未绑定函数包装为绑定方法。但是它们都包装了相同的功能。

这是您的示例扩展以显示正在发生的事情。

class A(object):
  def __init__(self, flag):
    self.flag = flag

  def func(self):
    print self.flag

a = A(1)
b = A(2)

callback_a = a.func
callback_b = b.func

print "typeof(callback_a) = {0}".format(type(callback_a))
print "typeof(callback_b) = {0}".format(type(callback_b))

print "typeof(callback_a.__func__) = {0}".format(type(callback_a.__func__))
print "typeof(callback_b.__func__) = {0}".format(type(callback_b.__func__))

print "'callback_a.__func__ is callback_b.__func__'  is {0}".format(callback_a.__func__ is callback_b.__func__)

callback_a()
callback_b()

此代码输出

typeof(callback_a) = <type 'instancemethod'>
typeof(callback_b) = <type 'instancemethod'>
typeof(callback_a.__func__) = <type 'function'>
typeof(callback_b.__func__) = <type 'function'>
'callback_a.__func__ is callback_b.__func__'  is True

您可以使用is运算符清楚地看到两个instancemethod类共享同一个函数对象。

于 2012-04-04T01:42:59.113 回答