15

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 无法myvarfunc. 即使myvar在外部范围的本地字典中,Python 也不会将其复制到内部范围的本地字典中。原因如下:

每当 Python 查找变量名时,首先检查locals,然后检查globals,最后检查builtins。在模块范围内locals并且globals是相同的字典对象。所以x = 5模块范围内的语句将放在x字典中locals,字典也是globals字典。现在,在模块范围内定义的需要查找的函数x不会x在函数范围内找到locals,因为 Python 不会将模块范围的本地变量复制到函数范围的本地变量中。但这通常不是问题,因为它可以xglobals.

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()


所以这一切的结果是,在模块范围内执行代码时,您必须确保全局字典和本地字典是相同的对象,否则模块范围函数将无法访问模块范围变量。

但就我而言,我不希望全局字典和本地字典相同。所以我需要一些方法来告诉 Python 解释器我正在函数范围内执行代码。有没有办法做到这一点?我查看了PyCompileFlags以及其他参数,PyEval_EvalCodeEx但找不到任何方法来做到这一点。

4

1 回答 1

5

Python 实际上并没有将外部范围的局部变量复制到内部范围的局部变量中。国家文件locals

自由变量在函数块中调用时由 locals() 返回,而不是在类块中。

这里的“自由”变量是指被嵌套函数封闭的变量。这是一个重要的区别。

对于您的情况,最简单的解决方法是传递与and相同的dict 对象:globalslocals

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], {}, {})
于 2012-09-04T15:37:50.530 回答