eval'd语句似乎并未实际在具有相应全局变量和局部对象的环境中执行。
def f(x):
return g(x)*3
def g(x):
return x**2
funcs = {"f":f,"g":g}
del globals()['g'] # to keep them out of the global environment
del globals()['f']
eval("f(2)",globals(),funcs)
错误:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
File "<stdin>", line 2, in f
NameError: name 'g' is not defined
更新
更多插图:
>>> exec("print(globals()['g'])",{**globals(),**funcs})
<function g at 0x7feb627aaf28>
>>> eval("f(2)",{**globals(),**funcs})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in <module>
File "<stdin>", line 2, in f
NameError: name 'g' is not defined
修改
这不是this question的重复。即使作为全局函数传递,函数g也无法查找。
答案 0 :(得分:3)
通过使用f()
查看从函数dis.dis(f)
定义编译的字节码可以看出问题:
7 0 LOAD_GLOBAL 0 (g)
2 LOAD_FAST 0 (x)
4 CALL_FUNCTION 1
6 LOAD_CONST 1 (3)
8 BINARY_MULTIPLY
10 RETURN_VALUE
如您所见,第一条指令尝试加载名为g
的全局。
使其成功的一种方法是使g()
成为本地函数:
def f(x):
def g(x):
return x**2
return g(x)*3
funcs = {"f": f}
del globals()['f'] # to keep it out of the global environment
print(eval("f(2)", globals(), funcs)) # -> 12
以下是修订版f()
中的字节码进行比较:
8 0 LOAD_CONST 1 (<code object g at 0x00546288, file "test.py">)
2 LOAD_CONST 2 ('f.<locals>.g')
4 MAKE_FUNCTION 0
6 STORE_FAST 1 (g)
11 8 LOAD_FAST 1 (g)
10 LOAD_FAST 0 (x)
12 CALL_FUNCTION 1
14 LOAD_CONST 3 (3)
16 BINARY_MULTIPLY
18 RETURN_VALUE
答案 1 :(得分:2)
当您定义f
时,它决定对g
的引用是全局名称查找。此外,它在定义时保留了对有效全局环境的引用(这使得您可以在不剥夺其原始全局变量的情况下调用其他模块中的函数)。
当您删除g
后,您从根本上打破了f
- g
的全局查询现在将失败。
exec
调用的全局/本地环境参数对已编译的函数f
没有任何影响。它们只会影响您执行的实际文字:"f(2)"
。换句话说,实际使用您提供的环境的唯一名称查找是f
本身的名称。