Him
Him

Reputation: 5551

eval globals and locals argument do not work as expected

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

Answers (2)

martineau
martineau

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

jasonharper
jasonharper

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

Related Questions