Reputation: 5551
The statement eval'd does not seem to actually be executed in an environment with the corresponding globals and locals objects.
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)
Error:
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
Update:
More illustration:
>>> 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
Edit
This is not a duplicate of this question. The function g fails to look up even when passed as a global.
Upvotes: 3
Views: 1158
Reputation: 123463
The problem is can be seen by looking at the bytecode compiled from the
definition of function f()
using 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
As you can see, the first instruction tries to load a global named g
.
One way to make it work would be to make g()
a local function:
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
Here's the bytecode from the revised f()
for comparison:
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
Upvotes: 3
Reputation: 9597
When you defined f
, it decided that the reference to g
was a global name lookup. Furthermore, it retained a reference to the global environment in effect at the point of definition (that's what lets you call functions in other modules without depriving them of their original globals).
When you then deleted g
, you fundamentally broke f
- this global lookup of g
will now fail.
The global/local environment parameters to your exec
call do not have ANY effect whatsoever on the already-compiled function f
. They only affect the actual text that you exec'ed: "f(2)"
. In other words, the only name lookup that actually uses the environment you provided is that of f
itself.
Upvotes: 2