我需要在 python 中编写一个自定义 exec 函数(出于多种目的,但这不是这里的问题,所以这个名为
myExec
的自定义 exec 的功能将与现在的
exec
完全一样)。
我进入了这个问题:
def myExec(code):
exec(code)
code = """
a = 1
print(a)
u = [a for x in range(3)]
print(u)
"""
myExec(code)
运行这个程序给出
1
Traceback (most recent call last):
File "___.py", line 12, in <module>
myExec(code)
File "___.py", line 2, in myExec
exec(code, globals(), locals())
File "<string>", line 4, in <module>
File "<string>", line 4, in <listcomp>
NameError: name 'a' is not defined
所以
print(a)
没有任何问题。但错误发生在
u = [a for x in range(3)]
行上。当生成器对象转换为列表时,名称
a
似乎未定义。
请注意,如果该行是
u = [a, a, a]
,则不会引发错误。如果我们使用
exec
而不是
myExec
任何原因以及如何解决这个问题?
出现此问题的原因是,列表推导式(list comprehensions)的作用域规则与 Python 中的其他代码块略有不同。在你的情况下,列表推导式
[a for x in range(3)]
在
myExec
函数的
exec
调用中被执行,而不是在
myExec
函数的顶层执行。
当
exec
函数被调用时,它会创建一个新的作用域。列表推导式内的代码在这个新的作用域中执行,而不是在定义列表推导式的作用域中执行。因此,列表推导式无法访问在
exec
函数创建的作用域之外定义的变量。
为了解决这个问题,你可以将
locals()
和
globals()
传递给
exec
函数,以便它可以在当前作用域中执行代码:
def myExec(code):
exec(code, globals(), locals())
code = """
a = 1
print(a)
u = [a for x in range(3)]
print(u)
"""
myExec(code)
这样,列表推导式就能够访问在
myExec
函数中定义的
a
变量了。
需要注意的是
,直接使用
exec
和
eval
函数处理来自不受信任来源的代码可能会带来安全风险。如果你的代码需要处理不受信任的输入,请考虑使用更安全的替代方案,例如:
-
使用
ast.literal_eval
来解析简单的 Python 字面量。 -
使用沙箱环境,例如
RestrictedPython
来限制代码可以执行的操作。