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?
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.
Follow-up:
if you want to re-assign a value to b
in f2
you will need the global
keyword:
def f1(glob, loc):
exec("b = 5", glob, loc)
def f2():
f1(globals(), globals())
global b
b += 1
print(b + 1) # 7
f2()
Note that this has nothing to do with exec and passing around locals() / globals() - you can reduce it to this example, which would also fail w/o the global
keyword (it would throw an UnboundLocalError: local variable 'b' referenced before assignment
error).
b = 5
def f2():
global b
b += 1
print(b + 1) # 7
f2()
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments