Python如何区分作为类成员的回调函数?

时间:2012-04-04 01:31:29

标签: python

请看一个简单的例子:

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中的所有内容都是一个对象。所以在我的例子中,是否有两个具有不同参数但只有一个代码副本的函数实例?

2 个答案:

答案 0 :(得分:19)

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

Python在内存中只需要func()的一个实现,但是它可能会在运行时创建一个或多个“trampoline”函数来完成绑定(我不确定这个内部细节,并且它不管怎么说,Python实现之间会有所不同。)

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

答案 1 :(得分:16)

特定于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类共享相同的函数对象。