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

fly*_*pen 27 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()
Run Code Online (Sandbox Code Playgroud)

结果是:

1
2
Run Code Online (Sandbox Code Playgroud)

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

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

  2. 我记得Python中的所有内容都是一个对象.所以在我的例子中,是否有两个具有不同参数但只有一个代码副本的函数实例?

Gre*_*ill 19

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

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

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

  • @jdi尝试`a_func = a.func; b_func = b.func; id(a_func)== id(b_func)`.`a.func`和`b.func`是不同的绑定方法对象.在CPython中,绑定方法对象是在属性访问时构造的,而不是缓存的.当你执行`id(a.func)`时,会构造一个新的绑定方法,你得到它的id,然后立即进行垃圾收集,因为没有更多的引用.当你执行`id(b.func)`时,新的绑定方法是在同一块内存中构造的,所以你的相等是巧合的. (6认同)
  • @jdi:这纯粹是一个实现细节,但不能保证.例如,我只是在pypy和ironpython上尝试过它们并不相同. (3认同)
  • 如果你执行 `id(b.func) == id(a.func)` 你会发现它们指向相同的内存 (2认同)

aar*_*ing 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()
Run Code Online (Sandbox Code Playgroud)

此代码输出

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
Run Code Online (Sandbox Code Playgroud)

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

  • 我想知道为什么这会被贬低.如果客观上有错误,那么我想知道. (2认同)
  • `callback_a是callback_b`是'False`因为每个都是`instancemethod`,它包含函数**和**用于调用该方法的实例.两者共享相同的`__func__`(如初始帖子中所示),但每个都有一个不同的`self`对象实例,用于调用该函数.合理? (2认同)