I’m having a hard time understanding the scope of globals(), locals() in Python. Consider the following code:
def f1(glob, loc):
exec("b = 5", glob, loc)
f1(globals(), locals())
print(b + 1)
This passes globals and locals to f1 where the variable b = 5 is defined, and sure enough it’s present after the function call in the main scope. The code correctly prints
6
To my surprise, when doing the same in the scope of a function, f2 here, it fails:
def f1(glob, loc):
exec("b = 5", glob, loc)
def f2():
f1(globals(), locals())
print(b + 1)
f2()
Traceback (most recent call last):
File "/tmp/q.py", line 10, in <module>
f2()
File "/tmp/q.py", line 7, in f2
print(b + 1)
NameError: name 'b' is not defined
Why is that? Is it possible to generalize f1 to allow it to be called from f2 as well?
>Solution :
In your first example, locals is the globals dict, because you’re on a module level.
You can check that with print(id(globals()), id(locals()))
In your second example, locals is a different dict in the scope of the function.
So to make example 2 work, either use:
def f1(glob, loc):
exec("b = 5", glob, loc)
def f2():
f1(globals(), globals())
print(b + 1)
f2()
or
def f1(glob, loc):
exec("b = 5", glob, glob)
def f2(loc):
f1(globals(), loc)
print(b + 1)
f2(locals())
So this isn’t really a matter of exec() but of in which scope you’re calling locals().
And ofc it’s also a matter of what the code in exec does, because an assignment changes the scope of the name to the current (local) scope, unless global/nonlocal is involved. That’s why b=5 is executed in the local scope – except that in your first example, the "local" scope is the global scope.