Eval arguments globals and locals not working as expected

The eval'd operation does not seem to actually be done in an environment with matching 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)

      

Mistake:

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:

Additional 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 g function cannot appear even when passed as global.

+3


source to share


2 answers


When you determined f

, it decided that the link to g

was a global name lookup. In addition, it retained a reference to the global environment in effect at the point of definition (this is what allows functions to be called in other modules without stripping them of their original globals).

When you deleted g

, you fundamentally violated f

- this global search will g

now fail.



Global / local environment settings for your call exec

have no effect on an already compiled function f

. They affect only the actual text that you have completed: "f(2)"

. In other words, the only name search that actually uses the environment you provided is for f

.

+2


source


The problem is looking at the bytecode compiled from the function definition f()

with 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 is trying to load a global named g

.

One way to do this is to make a g()

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 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

      

+3


source







All Articles