在 exec() 中定义的回调期间关闭丢失

Closure lost during callback defined in exec()

这是我使用 Python 的第三天,所以请原谅新手的错误。所以这是我的工作代码。 person.test()向老板注册回调,老板调用回调,一切正常。

class Boss:
  def registerCallback(self,cb):
    self.cb = cb
  def doCallback(self):
    self.cb()

class Person:
  def woot(self,data):
    print("Woot! ",data)

  def test(self,boss,data):
    def callback ():
      self.woot(data)
    boss.registerCallback(callback)    

boss = Boss()
person = Person()
person.test(boss,1)
boss.doCallback()

但是,如果我将回调移动到 exec() 中,闭包就会丢失。回调运行,但 selfdata 未知,因此调用 self.woot(data)失败。

class Boss:
  def registerCallback(self,cb):
    self.cb = cb
  def doCallback(self):
    self.cb()

class Person:
  def woot(self,data):
    print("Woot! ",data)

  def test(self,boss,data):
    x = "def callback():\n  self.woot(data)\nboss.registerCallback(callback)"
    exec(x,globals(),locals())

boss = Boss()
person = Person()
person.test(boss,1)
boss.doCallback()

我也试过 compile() ,没有成功。有什么想法吗?我真的不想手动携带 self/data 的副本通过老板并返回,因为我的真实代码要复杂得多。我真的需要一种方法来保持闭包。

如果你只传递 locals(作为函数的全局数据),那么事情或多或少会起作用:

class Person:
  def woot(self,data):
    print("Woot! ",data)

  def test(self,boss,data):
    x = "def callback():\n  self.woot(data)\nboss.registerCallback(callback)"
    exec(x, locals())

当然,如果你也需要全局变量,你可以将它们打包在一起:

def test(self, boss, data):
  namespace = globals().copy()
  local_copy = locals().copy()
  namespace.update(local_copy)
  x = 'def foo(): pass'
  exec(x, namespace)

为什么您当前的代码失败了?

selfcallback 的自由变量,如果您阅读 locals() 的文档,您会发现:

Free variables are returned by locals() when it is called in function blocks, but not in class blocks.

现在来自 exec() 的文档:

If exec gets two separate objects as globals and locals, the code will be executed as if it were embedded in a class definition.

因此,当我们将两个不同的对象传递给 exec() 时,locals() 字典对于 callback() 实际上是空的,因为它无法再访问自由变量,因此 通过 locals()globals() 的合并版本应该为你做。