Using a function defined in an exec'ed string in Python 3

python, python-3.x, python-internals

Solution

Note: `exec` was just a Simple statement in Python 2.x, whereas it is a function in Python 3.x.

Python 2.7

Let us check the changes made by executing `a`.

class Test:
    def __init__(self):
        l, g = locals().copy(), globals().copy()
        exec a           # NOT a function call but a statement
        print locals() == l, globals() == g
        x()

t = Test()

Output

False True
42

It means that, it has changed something in the `locals` dictionary. If you print `locals().keys()` before and after the `exec`, you will see `x`, after `exec`. As per the documentation of exec,

In all cases, if the optional parts are omitted, the code is executed in the current scope.

So, it does exactly what the documentation says.

Python 3.x:

When we execute the same in Python 3.x, we get similar result, except we get that error.

class Test:
    def __init__(self):
        l, g = locals().copy(), globals().copy()
        exec(a)          # Function call, NOT a statement
        print(locals() == l, globals() == g)
        x()

Output

False True
NameError: name 'x' is not defined

Even the documentation of `exec` function says,

In all cases, if the optional parts are omitted, the code is executed in the current scope.

But it also includes a note at the bottom,

Note: The default locals act as described for function `locals()` below: modifications to the default locals dictionary should not be attempted. Pass an explicit locals dictionary if you need to see effects of the code on locals after function `exec()` returns.

So, we curiously check the `locals()` documentation and find

Note: The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter.

So, interpreter doesn't honor the changes made to the `locals()` object. That is why it is not recognizing `x` as defined in the local scope.

But when we do

def __init__(self):
    exec(a, globals())
    x()

it works, because we add it to the `globals` dictionary. Python tries to find `x` in local scope first and then in class scope and then in global scope and it finds it there. So it executes it without any problem.

Problem

Why does the following python3 code produces an error? ``` a=''' def x(): print(42) ''' class Test: def __init__(self): exec(a) x() t = Test() ``` Results in this message: ``` Traceback (most recent call last): File "bug.py", line 11, in <module> t = Test() File "bug.py", line 9, in __init__ x() NameError: global name 'x' is not defined ```

Original source

Related problems