Why did Python 3 changes to exec break this code?
backwards-compatibility, exec, metaprogramming, python, python-3.x
Solution
You can see the generated bytecode for each Python version with:
>>> from dis import dis
And, for each interpreter:
#Python 3.2
>>> dis(Testing.__init__)
...
5 10 LOAD_GLOBAL 1 (a_func)
...
#Python 2.7
>>> dis(Testing.__init__)
...
5 8 LOAD_NAME 0 (a_func)
...
As you can see, Python 3.2 searches for a global value (LOAD_GLOBAL) named `a_func` and 2.7 first searches the local scope (LOAD_NAME) before searching the global one.
If you do `print(locals())` after the `exec`, you'll see that `a_func` is created inside the `__init__` function.
I don't really know why it's done that way, but seems to be a change on how symbol tables are processed.
BTW, if want to create a `a_func = None` on top of your `__init__` method to make the interpreter know it's a local variable, it'll not work since the bytecode now will be `LOAD_FAST` and that don't make a search but directly gets the value from a list.
The only solution I see is to add `globals()` as second argument to `exec`, so that will create `a_func` as a global function an may be accessed by the `LOAD_GLOBAL` opcode.
Edit
If you remove the `exec` statement, Python2.7 change the bytecode from `LOAD_NAME` to `LOAD_GLOBAL`. So, using `exec`, your code will always be slower on Python2.x because it has to search the local scope for changes.
As Python3's `exec` is not a keyword, the interpreter can't be sure if it's really executing new code or doing something else... So the bytecode don't change.
E.g.
>>> exec = len
>>> exec([1,2,3])
3
tl;dr
`exec('...', globals())` may solve the problem if you don't care the result being added to global namespace
Problem
I looked through the myriad 'Python exec' threads on SO, but couldn't find one that answered my issue. Terribly sorry if this has been asked before. Here's my problem: ``` # Python 2.6: prints 'it is working' # Python 3.1.2: "NameError: global name 'a_func' is not defined" class Testing(object): def __init__(self): exec("""def a_func(): print('it is working')""") a_func() Testing() ``` ``` # Python 2.6: prints 'it is working' # Python 3.1.2: prints 'it is working' class Testing(object): def __init__(self): def a_func(): print('it is working') a_func() Testing() ``` As the standard function definition works in both Python versions, I'm assuming the problem must be a change to the way exec works. I read the API docs for 2.6 and 3 for `exec` and also read the "What's New In Python 3.0" page and couldn't see any reason why the code would break.