C Python:在上下文中运行Python代码

时间:2012-09-04 14:34:30

标签: python c python-3.x python-c-api cpython

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。在模块范围localsglobals是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的其他参数,但无法找到任何方法。

1 个答案:

答案 0 :(得分:4)

Python实际上并没有将外部区域本地复制到内部区域本地; locals州的文档:

  

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

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

对您的情况最简单的解决方法就是将相同的 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], {}, {})