inner() can access the non-local variable x in middle() with nonlocal x:
def outer():
x = 0
def middle():
x = 5 # <- Here
def inner():
nonlocal x # Here
x += 1
print(x) # 6
inner()
middle()
outer()
Now, how can inner() access the non-local variable x in outer()?
def outer():
x = 0 # <- Here
def middle():
x = 5
def inner():
x += 1
print(x) # How to print 1?
inner()
middle()
outer()
Reading PEP 3104 which introduced the
nonlocalkeyword, we learn two things:The inspiration was in part the ability of other languages to refer to names in an enclosing scope, specifically because they distinguish between assignment and declaration.
This ability is limited to referring to the next outer declaration in those languages, and there's no way to look past an intermediate redeclaration. This is known as "name hiding" or "shadowing".
So,
nonlocalwas never really intended to do what you ask.There was in fact a proposal to allow this with a syntax like
.xfor one scope out,..xfor two scopes out, etc.This was rejected as error-prone, and no other proposal tried to support that ability.
You can't do what you want directly in Python, and you can't do it in any of the languages that inspired
nonlocaleither. Best to just stop redeclaring the same name.There is however an ugly workaround, described in the PEP: manually re-exporting a name with a namespace object.