1

I used to create a exec statement to generate a function to use in Python2. However, when I move to Python3, the same approach stopped work ing.

Python 3.6.7 (default, Oct 22 2018, 11:32:17) 
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> def a():
...   exec(compile("def printt(): print(2)","printt","exec"))
...   printt()
... 
>>> a()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in a
NameError: name 'printt' is not defined
>>>

Python 2.7.15+ (default, Nov 27 2018, 23:36:35) 
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> def a():
...   exec(compile("def printt(): print(2)","printt","exec"))
...   printt()
... 
>>> a()
2
>>> 

1 Answers1

0

In Python 3, the exec function is no longer able to modify the local scope directly.

This is documented in exec(), see the note which says:

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.

Also note that the Python 2 code is using the exec statement, even though you wrote it to look like a function, it's a completely different form. See also this answer for a more in-depth discussion, or even this answer which covers this issue from a Python code disassembly point of view.

So, if you want to access the symbols defined in the scope of the exec(), you should pass it a dict (possibly empty, or pre-populated with any variable assignments or function definitions you want to use in your exec() block) and access it afterwards.

This code works in Python 3:

def a():
   namespace = {}
   exec(compile("def printt(): print(2)","printt","exec"),
        globals(), namespace)
   printt = namespace['printt']
   printt()

Hopefully this should be good enough for your purposes.

filbranden
  • 8,522
  • 2
  • 16
  • 32