Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 
like image 649
Gregwar Avatar asked Jul 14 '14 09:07

Gregwar


People also ask

Does exec () return anything Python?

Python exec() does not return a value; instead, it returns None. A string is parsed as Python statements, which are then executed and checked for any syntax errors. If there are no syntax errors, the parsed string is executed.

What is exec () in Python?

The exec() function executes the specified Python code. The exec() function accepts large blocks of code, unlike the eval() function which only accepts a single expression.

What is exec () and eval ()?

Eval() only evaluates the single expression, not the complex logic code, whereas Exec can be used to execute any number of expression. exec() accept the source code which contains statements like, for, while, print, import, class, if we pass these statement to eval() it will throw error.

How do you use def in Python 3?

A function is defined by using the def keyword, followed by a name of your choosing, followed by a set of parentheses which hold any parameters the function will take (they can be empty), and ending with a colon.


1 Answers

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

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.

like image 74
thefourtheye Avatar answered Sep 17 '22 13:09

thefourtheye