Python etc / nonlocal

Published: 02 March 2021, 18:00

Any enclosing variable can be shadowed in the local scope without affecting the global one:

v = 'global'
def f():
v = 'local'
print(f'f {v=}')
f()
# f v='local'

print(f'{v=}')
# v='global'


And if you try to use a variable and then shadow it, the code will fail at runtime:

v = 'global'
def f():
print(v)
v = 'local'
f()
# UnboundLocalError: local variable 'v' referenced before assignment


If you want to re-define the global variable instead of locally shadowing it, it can be achieved using global and nonlocal statements:

v = 'global'
def f():
global v
v = 'local'
print(f'f {v=}')
f()
# f v='local'
print(f'g {v=}')
# g v='local'

def f1():
v = 'non-local'
def f2():
nonlocal v
v = 'local'
print(f'f2 {v=}')
f2()
print(f'f1 {v=}')
f1()
# f2 v='local'
# f1 v='local'


Also, global can be used to skip non-local definitions:

v = 'global'
def f1():
v = 'non-local'
def f2():
global v
print(f'f2 {v=}')
f2()
f1()
# f2 v='global'


To be said, using global and nonlocal is considered a bad practice that complicates the code testing and usage. If you want a global state, think if it can be achieved in another way. If you desperately need a global state, consider using singleton pattern which is a little bit better.