小编典典

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

python

使用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是相同的字典对象。因此x = 5,模块作用域中的语句将放置xlocals字典中,该globals字典也是字典。现在,在模块作用域中定义的需要查找的函数x将不会x在function-
scope中找到locals,因为Python不会将模块作用域的局部变量复制到function-
scope的局部变量中。但这通常不是问题,因为它可以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但找不到任何方法可以做到这一点。


阅读 216

收藏
2020-12-20

共1个答案

小编典典

Python实际上并没有将外部作用域的本地复制到内部作用域的本地;locals状态文档:

当在功能块中调用免费变量时,它是由locals()返回的,而在类块中则没有。

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

您的情况,最简单的解决方法是只传递 同一个 字典对象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], {}, {})
2020-12-20