Закрытие потеряно во время обратного вызова, определенного в 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(), закрытие теряется. Обратный вызов выполняется, но self и данные неизвестны, поэтому вызов 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()
Я попытался скомпилировать () тоже, не повезло. Какие-нибудь мысли? Я действительно не хочу вручную переносить копию себя / данных через босса и обратно, потому что мой реальный код гораздо более запутанный. Мне действительно нужен способ сохранить закрытие.
2 ответа
Если вы только проходите 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)
Почему ваш текущий код не работает?
self
является свободной переменной для callback
и если ты читаешь locals()
Документацию вы найдете:
Свободные переменные возвращаются
locals()
когда он вызывается в функциональных блоках, но не в блоках классов.
А теперь из exec()
Документация:
Если
exec
получает два отдельных объекта как глобальные и локальные, код будет выполняться так, как если бы он был встроен в определение класса.
Итак, когда мы передаем два разных объекта exec()
locals()
словарь на самом деле пуст для callback()
поскольку он больше не может получить доступ к свободным переменным, следовательно, решение, предложенное @mgilson, передает объединенную версию locals()
а также globals()
должен сделать это для вас.