问题描述
我很好奇exec
函数中的字符串是否可以exec
直接替换(使用适当的缩进)。我知道在 99.9% 的情况下,您不应该使用exec
,但我更感兴趣的是是否可以这样做,而不是是否应该这样做。
我想要的行为相当于:
GLOBAL_CONSTANT = 1
def test_func():
def A():
return GLOBAL_CONSTANT
def B():
return A()
return B
func = test_func()
assert func() == 1
但我得到的是:
GLOBAL_CONSTANT = 1
EXEC_STR = """
def A():
return GLOBAL_CONSTANT
def B():
return A()
"""
def exec_and_extract(exec_str, var_name):
# Insert code here
func = exec_and_extract(EXEC_STR, 'B')
assert func() == 1
失败的尝试
def exec_and_extract(exec_str, var_name):
exec(EXEC_STR) # equivalent to exec(EXEC_STR, globals(), locals())
return locals()[var_name]
NameError: name 'A' is not defined
当调用func()
sinceA
并且B
存在于exec_and_extract
'slocals()
但运行时的执行上下文时A
是B
' exec_and_extract
s globals()
。
def exec_and_extract(exec_str, var_name):
exec(EXEC_STR, locals()) # equivalent to exec(EXEC_STR, locals(), locals())
return locals()[var_name]
NameError: name 'GLOBAL_CONSTANT' is not defined
A
当从内部调用时,因为是'sfunc()
的执行上下文不包含.A
exec_and_extract
locals()
GLOBAL_CONSTANT
def exec_and_extract(exec_str, var_name):
exec(EXEC_STR, globals()) # equivalent to exec(EXEC_STR, globals(), globals())
return globals()[var_name]
有效但污染全局命名空间,不等效。
def exec_and_extract(exec_str, var_name):
locals().update(globals())
exec(EXEC_STR, locals()) # equivalent to exec(EXEC_STR, locals(), locals())
return locals()[var_name]
有效,但需要将exec_and_extract
's的全部内容复制globals()
到locals()
其中,如果很大,则浪费时间globals()
(当然不适用于这个人为的示例)。此外,与“粘贴代码”版本略有不同,因为如果其中一个参数exec_and_extract
碰巧是GLOBAL_CONSTANT
(一个糟糕的参数名称),则行为会有所不同(“粘贴”版本将使用参数值而这代码将使用全局常量值)。
进一步的限制
试图掩盖问题陈述中的任何“漏洞”:
- 该
exec_str
值应该代表可以访问全局或局部范围变量的任意代码。 - 解决方案不应要求分析在
exec_str
. - 后续调用之间不应该有“污染”
exec_and_extract
(在全局命名空间或其他地方)。即在这个例子中,执行不EXEC_STR
应该留给A
将来调用exec_and_extract
.