提问者:小点点

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


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查找变量名时,它首先检查局部变量,然后检查全局变量,最后检查内置变量。在模块范围内,localsglobals是相同的字典对象。因此,模块范围内的语句x=5x放在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的附加参数,但找不到任何方法。


共1个答案

匿名用户

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], {}, {})

相关问题