1

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()
Super Kai - Kazuya Ito
  • 22,221
  • 10
  • 124
  • 129
  • About `nonlocal`, see for ex. https://stackoverflow.com/questions/8447947/is-it-possible-to-modify-a-variable-in-python-that-is-in-an-outer-enclosing-b or https://stackoverflow.com/questions/1261875/python-nonlocal-statement-keyword – Thierry Lathuille Jan 08 '23 at 11:22
  • 2
    It can use it, but only if there are no conflicting names in a closer scope. Just use a different name. Why would you want to use the same name anyway? – Tom Karzes Jan 08 '23 at 11:23
  • The same issues as your [previous question](https://stackoverflow.com/q/75048481/1126841) apply: you have control over the names of the inner variables, so *don't* shadow the non-local variables you need access to. That is, if `inner` needs to access `x` from `outer`, don't use `x` as the name of a local variable in `middle`. – chepner Jan 08 '23 at 20:00

1 Answers1

1

Reading PEP 3104 which introduced the nonlocal keyword, we learn two things:

  1. 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, nonlocal was never really intended to do what you ask.

  2. There was in fact a proposal to allow this with a syntax like .x for one scope out, ..x for 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 nonlocal either. 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.

Useless
  • 64,155
  • 6
  • 88
  • 132