Python C API函数PyEval_EvalCode
让您执行已编译的Python代码。我想执行一段Python代码,好像它是在一个函数范围内执行,这样它就有了自己的局部变量字典,不会影响全局状态。
这似乎很容易做到,因为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也不会将其复制到内部作用域中的本地字典中。原因如下:
每当Python查找变量名称时,首先检查locals
,然后检查globals
,最后检查builtins
。在模块范围,locals
和globals
是SAME字典对象。因此,模块范围内的语句x = 5
会将x
放在locals
字典中,该字典也是globals
字典。现在,在模块范围定义的需要查找x
的函数在函数范围x
内找不到locals
,因为Python不会将模块范围的本地复制到函数中 - 范围本地人。但这通常不是问题,因为它可以在x
中找到globals
。
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
的其他参数,但无法找到任何方法。
答案 0 :(得分:4)
Python实际上并没有将外部区域本地复制到内部区域本地; locals
州的文档:
当在功能块中调用时,locals()返回自由变量,而不是在类块中调用。
这里“自由”变量是指由嵌套函数关闭的变量。这是一个重要的区别。
对您的情况最简单的解决方法就是将相同的 dict对象传递给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], {}, {})