pythoncapi函数PyEval\u EvalCode
让我们执行编译好的Python代码。我希望执行一块Python代码,就像它在函数范围内执行一样,这样它就有了自己的局部变量字典,而不会影响全局状态。
这似乎很容易做到,因为PyEval\u EvalCode
允许您提供全局和本地字典:
PyObject*PyEval\u EvalCode(PyCodeObject*co,PyObject*globals,PyObject*locals)
我遇到的问题与Python如何查找变量名有关。考虑下面的代码,我用<代码> PyValueValueCudio>代码执行:
myvar = 300
def func():
return myvar
func()
这个简单的代码实际上引发了一个错误,因为Python无法从func
中找到变量myvar
。即使myvar
位于外部作用域的本地字典中,Python也不会将其复制到内部作用域的本地字典中。原因如下:
每当Python查找变量名时,它首先检查局部变量
,然后检查全局变量
,最后检查内置变量
。在模块范围内,locals
和globals
是相同的字典对象。因此,模块范围内的语句x=5
将x
放在locals
字典中,这也是globals
字典。现在,在需要查找x
的模块作用域中定义的函数在函数作用域locals
中找不到x
,因为Python不会将模块作用域局部变量复制到函数作用域局部变量中。但这通常不是问题,因为它可以在globals
中找到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\u evalcodex
的附加参数,但找不到任何方法。
Python实际上并没有将外部作用域局部变量复制到内部作用域局部变量中;本地人的文档说明:
在函数块中调用自由变量时,它由locals()返回,而不是在类块中。
这里的“自由”变量是指由嵌套函数关闭的变量。这是一个重要的区别。
对于您的情况,最简单的修复方法就是传递与全局
和局部
相同的dict对象:
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], {}, {})