如何在给定的上下文(本地和全局)中执行可调用?

时间:2017-04-07 01:38:19

标签: python python-3.x

我有一个callback可调用,它是在exec()内计算的。

我在此_globals中使用了_localsexec()variable_from_context中有一个键为_locals的值。

但是当我尝试调用callback()时,它在当前上下文中执行,而不是在其原始上下文中执行。找不到variable_from_context

exec()只接受str或代码对象。我发现这种方式可以访问callback的代码对象,并尝试调用它:

exec(callback.__code__, scenario._globals, scenario._locals)

但是,我得到NameError: name 'variable_from_context' is not defined

使用给定的callbackglobals致电locals的正确方法是什么?

代码示例:

der_callback = None

def save_callback(cb):
    der_callback = cb

_locals = {}
_globals = {'save_callback': save_callback}

text = r'''
zoo = ['we']

def foo():
    print(zoo[0])

save_callback(foo)
'''

exec(text, _globals, _locals)
print(_locals)

# One can also try this:
# exec(_locals['foo'].__code__, _globals, _locals)

# EDIT: Now, why doesn't _locals get used when invoking a function?
# This works:
# exec('print(zoo[0])', _globals, _locals)

# EDIT continues: This doesn't:
# exec('foo()', _globals, _locals)

_locals['foo']()

输出:

Traceback (most recent call last):
{'zoo': ['we'], 'foo': <function foo at 0x1020b2d90>}
  File "/Users/me/pyzoo/callback_from_eval.py", line 22, in <module>
    exec(_locals['foo'].__code__, _globals, _locals)
  File "<string>", line 5, in foo
NameError: name 'zoo' is not defined

2 个答案:

答案 0 :(得分:2)

首先,任何上下文都有其全局变量和本地变量。在模块级别,它们是等效的(相同的字典)。在其他情况下,它们通常不同。除非您将某些内容声明为global,否则任何赋值都会修改上下文本地,但不会修改全局变量。

然后,任何函数都有自己的本地。因此,在输入foo时,_globals被视为全局,但_locals不被视为本地人。因此,zoo不可见。直接修复是在分配到块中之前说global zoo

另外,您在save_callback中暴露了同样的问题:赋值给der_callback并不会改变全局变量,而是一个在退出后立即丢失的局部变量。要修复,请在函数内声明der_callback为全局。

UPD:如果您需要chunk-top locals,您可以尝试以下技巧之一:

T1。在chunk global level上,为locals()指定一个名称,并在foo()中重用它:

globals()['xl'] = locals()
zoo = ['we']

def foo():
    print(xl['zoo'][0])

T2。使用调用堆栈帧查找:

在大块之外:

def save_callback(cb):
    global der_callback
    der_callback = lambda: cb(sys._getframe(1).f_locals)

在foo定义中:

def foo(xl):
    print(xl['zoo'][0])

答案 1 :(得分:1)

正如Netch所说,你的

def save_callback(cb):
    der_callback = cb

分配给全局der_callback。它将cb对象绑定到本地名称der_callback,当然,当函数退出时,该绑定将丢失。要从函数内部分配全局对象,必须使用global指令。

也许这段代码可以帮助您了解正在发生的事情。

der_callback = None

def save_callback(cb):
    global der_callback 
    der_callback = cb

def show_dict(d, name):
    print(name)
    for k in sorted(d.keys()):
        if not k[0] == '_':
            print('{!r}: {!r}'.format(k, d[k]))
    print()

text = r'''
zoo = ['we']

def foo():
    print(zoo[0])

save_callback(foo)

show_dict(globals(), 'text GLOBALS')
'''

_globals = {
    'save_callback': save_callback,
    'show_dict': show_dict,
}
exec(text, _globals)

zoo = ['hello']

show_dict(globals(), 'module GLOBALS')

print(der_callback)
der_callback()

<强>输出

text GLOBALS
'foo': <function foo at 0xb71d5cd4>
'save_callback': <function save_callback at 0xb725553c>
'show_dict': <function show_dict at 0xb71d5c8c>
'zoo': ['we']

module GLOBALS
'der_callback': <function foo at 0xb71d5cd4>
'save_callback': <function save_callback at 0xb725553c>
'show_dict': <function show_dict at 0xb71d5c8c>
'text': "\nzoo = ['we']\n\ndef foo():\n    print(zoo[0])\n\nsave_callback(foo)\n\nshow_dict(globals(), 'text GLOBALS')\n"
'zoo': ['hello']

<function foo at 0xb71d5cd4>
we

在这两种情况下,locals()globals()相同,您可以将locals()传递给show_dict来查看。

以下是您的代码的略微修改版本,我认为它可以满足您的需求。

der_callback = None

def save_callback(cb):
    der_callback = cb

_globals = {'save_callback': save_callback}

text = r'''
zoo = ['we']

def foo():
    print(zoo[0])

save_callback(foo)
'''

exec(text, _globals)
_globals['foo']()

<强>输出

we

exec调用相当于

exec(text, _globals, None)