s = """
a=0
def func():
print(a)
func()
"""
g = {}
l = {}
exec(s, g, l)
Expected behavior:
Output: 0 Error:
Traceback (most recent call last):
File "x:\python\python_lab\test.py", line 11, in <module>
exec(s, g, l)
File "<string>", line 5, in <module>
File "<string>", line 4, in func
NameError: name 'a' is not defined
I checked the bytecode. It seems name “a” is in “local” namespace and print(a) trys to get “a”
from “global” namespace and causes the NameError.
It is correct that assignments go into the locals. It is correct that a reference in a function body, where there is no assignment to the same variable, should be sought in the global dictionary.
You don’t normally encounter this surprise because text that looks like s is normally executed as a module, where these two dictionaries are the same for the module level.
The other place code like s gets executed is in the definition of a class. In that case, the global and local dictionaries are distinct, as in your experiment. The globals are the globals of the scope containing the class definition, while the locals are a new dictionary that eventually becomes the dictionary of the class, the dictionary defining its methods and attributes.
It’s all very clever. But you really have to know what you’re doing when you supply separate dictionaries to exec().
You’re correct: that did indeed end up in the locals. Since you passed a third argument to exec(), the context is assumed to be a function - it has separate locals and globals. That’s why you’re seeing STORE_NAME rather than STORE_GLOBAL. If you were to put this in a full module, it would look something like this:
class execme:
a=0
def func():
print(a)
func()
Instead, try passing just ONE dictionary to exec:
>>> exec(s, g)
0
Now the code executes at module level, and everything happens as you would expect it to.