我试图在project中使用Python exec
来执行嵌入式Python代码。
我遇到的问题是在exec
语句中模块级创建的变量无法访问来自同义词中定义的函数模块。
假设你有以下Python程序:
x = 5
def foo():
print x
foo()
如果你将上面四行放在一个文件中并运行它,那就没问题了。
但是,如果您尝试在exec
声明中运行同一段代码,那么它将无法正常工作。
这是我们之前的程序,在exec
声明中:
import __builtin__
global_env = {'__builtins__': __builtin__}
local_env = dict()
exec """
x = 5
def foo():
print x
foo()
""" in global_env, local_env
执行时,它会产生以下错误,而不是工作:
Traceback (most recent call last):
File "lab.py", line 94, in <module>
""" in global_env, local_env
File "<string>", line 5, in <module>
File "<string>", line 4, in foo
NameError: global name 'x' is not defined
我认为模块级变量是全局存储的,但似乎至少在exec
中,它们并非如此。
例如,在上一个示例中,如果将foo()
的调用替换为:
print global_env
print local_env
你得到:
{'__builtins__': <module '__builtin__' (built-in)>}
{'x': 5, 'foo': <function foo at 0x102c12938>}
因此,模块级别中定义的任何内容(包括x
)都存储在locals()
中。
但是除了模块级别(x
语句)之外,无法从任何地方访问exec
。特别是,正如我们上面所看到的,x
的局部范围对于在同一exec
语句中定义的函数是不可见的。
我找到了两种方法来解决此问题,并再次访问x
。
第一个是在函数中使用global
关键字:
exec """
x = 5
def foo():
global x
print x
foo()
""" in global_env, local_env
第二个是在globals()
中使用locals()
和exec
的相同词典:
exec """
x = 5
def foo():
print x
foo()
""" in global_env
但是,这些只是解决原始问题的一半修复/解决方法。
所以我的问题是:为什么exec
中的模块级变量存储在本地,而为什么除了模块级之外的任何地方都无法访问?
一些密切相关的StackOverflow帖子:
答案 0 :(得分:6)
要了解发生了什么,您需要非常仔细地阅读the docs。关键部分说:
如果给出两个单独的对象作为全局变量和局部变量,则代码将被执行,就像它嵌入在类定义中一样。
这意味着本地赋值将进入本地命名空间(相当于类级变量),但如果函数(即方法)尝试引用本地(类)变量,则它们不会成为闭包。
将您的代码与:
进行比较class Test(object):
x = 1
def foo():
print x
foo()
出于同样的原因,你会得到同样的错误。 foo
不是闭包,因此它尝试在全局命名空间中引用x
(失败)。
答案 1 :(得分:4)
您看到的行为是well documented:
在所有情况下,如果省略可选部分,则执行代码 在目前的范围内。如果只有in之后的第一个表达式 指定,它应该是一个字典,将用于两者 全局和局部变量。如果给出两个表达式,它们就是 分别用于全局变量和局部变量。如果提供, locals可以是任何映射对象。请记住在模块级别,
globals
和locals
是相同的字典。 如果是两个单独的对象 作为全局变量和本地变量给出,代码将像执行一样执行 被嵌入到课程定义中。
事实上:
In [1]: class Test:
...: x = 5
...: def foo():
...: print(x)
...: foo()
...:
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-1-f20229bce3a1> in <module>()
----> 1 class Test:
2 x = 5
3 def foo():
4 print(x)
5 foo()
<ipython-input-1-f20229bce3a1> in Test()
3 def foo():
4 print(x)
----> 5 foo()
6
<ipython-input-1-f20229bce3a1> in foo()
2 x = 5
3 def foo():
----> 4 print(x)
5 foo()
6
NameError: name 'x' is not defined
您看到的行为是预期的。如果您想要像在模块级别那样执行代码,那么必须对全局和本地使用相同的对象,因此您的解决方法就是您应该做的。
答案 2 :(得分:3)
您的问题包含x
是模块级变量的假设,但实际上并非如此。
在模块级别globals()
和locals()
是相同的(这可能是CPython特有的),而在功能范围内它们是不同的:
def foo():
print globals() is locals()
print globals() is locals()
foo()
exec
语句指定范围并不在模块级执行代码,它会使用您告诉它执行的范围执行它。因此,您的上一个代码段不是半修复或解决方法,它正好解决了这个问题。
以下内容在模块级别显示时也有效。它与您的任何代码具有不同的含义,因为它分配给模块中的x
和foo
,因此 使x
成为模块级变量:
exec '''
x = 5
def foo():
print x
foo()
'''
最后一个代码出现在函数中时不起作用的原因,以及为什么你的第一次尝试不起作用,这是Blckknght的答案所说的。具有单独范围的exec
执行&#34;就好像在类定义&#34;中,而不是&#34;就像在模块中一样&#34;或者&#34;好像在一个函数&#34;中。这意味着您在其中定义的任何功能都无法访问其周围的命名空间。他们访问自己的本地命名空间和exec语句的全局命名空间,这两者都不是定义x
的地方。