【发布时间】:2020-04-24 05:22:31
【问题描述】:
问题描述
我很好奇是否可以在函数中使用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() 时,因为A 和B 存在于exec_and_extract 的locals() 中,但是运行A 或B 时的执行上下文是exec_and_extract 的@987654 @。
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 从func() 内部调用A 时,因为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 的globals() 的全部内容复制到其locals() 中,如果globals() 很大(当然不适用于这个人为的示例),这是浪费时间。此外,与“粘贴代码”版本略有不同,因为如果 exec_and_extract 的参数之一恰好是 GLOBAL_CONSTANT(一个可怕的参数名称),则行为会有所不同(“粘贴”版本会使用参数值,而此代码将使用全局常量值)。
进一步的限制
试图掩盖问题陈述中的任何“漏洞”:
-
exec_str值应代表可以访问全局或局部范围变量的任意代码。 - 解决方案不应要求分析在
exec_str中访问了哪些全局范围变量。 - 在后续调用
exec_and_extract(在全局命名空间或其他地方)之间不应存在“污染”。即在此示例中,EXEC_STR的执行不应留下A以供将来调用exec_and_extract时参考。
【问题讨论】:
-
"如果 globals() 很大,那是浪费时间" 不是真的,不是。我的意思是,你意识到,实际上并没有复制任何对象。
-
@juanpa.arrivillaga 是的,当然,只为其他所有内容复制原语和对象“引用”的值(不确定正确的术语是什么)。这可能没有我想象的那么昂贵,但也存在
user2357112 supports Monica在下面指出的问题,它拍摄了globals()的快照并且看不到这些变量的任何未来更新。 -
不,它不会复制“primitives”的值,python 没有“primitives”,一切都是对象,这里一切都使用引用语义。但是,是的,全局快照问题确实存在。我只是说,这绝对不是一项昂贵的手术。最多,一毫秒?这可能是一个异常大的
globals(),否则你在任何现代机器上都需要数百纳秒。 -
感谢您澄清 python 复制语义。
标签: python python-3.x python-exec