使用Python C API函数,PyEval_EvalCode您可以执行已编译的Python代码。我想像 在函数范围内执行一样执行 Python代码块,以便它具有自己的局部变量字典,该字典不影响全局状态。
PyEval_EvalCode
这似乎很容易做到,因为PyEval_EvalCode可以提供全局和本地字典:
PyObject* PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals)
我遇到的问题与Python如何查找变量名有关。考虑以下我执行的代码PyEval_EvalCode:
myvar = 300 def func(): return myvar func()
这个简单的代码实际上引发了错误,因为Python无法myvar从内部找到变量func。即使myvar位于外部作用域的本地字典中,Python也不会将其复制到内部作用域的本地字典中。原因如下:
myvar
func
每当Python查找变量名时,首先检查locals,然后检查globals,最后检查builtins。在 模块范围 ,locals并且globals是相同的字典对象。因此x = 5,模块作用域中的语句将放置x在locals字典中,该globals字典也是字典。现在,在模块作用域中定义的需要查找的函数x将不会x在function- scope中找到locals,因为Python不会将模块作用域的局部变量复制到function- scope的局部变量中。但这通常不是问题,因为它可以x在中找到globals。
locals
globals
builtins
x = 5
x
x = 5 def foo(): print(x) # This works because 'x' in globals() == True
只有 嵌套 函数,Python才能将外部作用域的本地复制到内部作用域的本地。(仅在内部范围内需要它们时,它似乎也很懒惰。)
def foo(): x = 5 def bar(): print(x) # Now 'x' in locals() == True bar()
因此,所有这些的结果是,在 模块范围内 执行代码时,必须确保全局字典和本地字典是SAME对象,否则模块作用域函数将无法访问模块作用域变量。
但就我而言,我不希望全局词典和本地词典相同。因此,我需要某种方式告诉Python解释器我正在函数作用域内执行代码。有什么办法可以做到这一点?我查看了PyCompileFlags和的其他参数,PyEval_EvalCodeEx但找不到任何方法可以做到这一点。
PyCompileFlags
PyEval_EvalCodeEx
Python实际上并没有将外部作用域的本地复制到内部作用域的本地;locals状态文档:
当在功能块中调用免费变量时,它是由locals()返回的,而在类块中则没有。
这里的“自由”变量是指由嵌套函数封闭的变量。这是一个重要的区别。
您的情况,最简单的解决方法是只传递 同一个 字典对象globals和locals:
code = """ myvar = 300 def func(): return myvar func() """ d = {} eval(compile(code, "<str>", "exec"), d, d)
否则,您可以将代码包装在一个函数中,然后从编译的对象中提取出来:
s = 'def outer():\n ' + '\n '.join(code.strip().split('\n')) exec(compile(s, '<str>', 'exec').co_consts[0], {}, {})