0
x = 0
def outer():
    x = 1
    def inner():
        nonlocal x
        x = 2
        def vnat():
            nonlocal x
            x = 5
            print('vnat:', x)

        vnat()
            
        print('inner:', x)

    
    inner()
    print('outer:', x)


outer()
print('global:', x)

Here is the result:

vnat: 5
inner: 5
outer: 5
global: 0

Why is def outer() taking the value of def vnat() (5)? And how can I specify def outer() with value of nonlocal x from def inner() [2]?

Output I need:

vnat: 5
inner: 5
outer: 2
global: 0
khelwood
  • 55,782
  • 14
  • 81
  • 108
  • I don't know if its the same case, but there's a bug in python's scopes.... see here: https://twitter.com/gvanrossum/status/1354305179244392453 – Ofer Sadan Feb 04 '21 at 10:55
  • @OferSadan This is not a bug, it's just a special feature of class definitions. Here, unbound local names are looked up in the *global* scope (see also [this answer](https://stackoverflow.com/a/64974318/3767239)). Whether this is desirable/intuitive is another question. – a_guest Feb 04 '21 at 11:27
  • "And how can I specify def outer() with value of nonlocal x from def inner() [2]?" I can't understand. If we do `x = 5` from `vnat`, *and `vnat` is using the `x` from `inner`*, then it stands to reason that the `x` from `inner` will be `5`, rather than `2`. The reason that running `inner` from `outer` doesn't cause `outer`'s `x` to be `2` is that **it was, but then `inner` called `vnat` to change it again**. – Karl Knechtel Sep 10 '22 at 10:48

1 Answers1

1

Whenever you specify nonlocal x it will refer to the name of an enclosing scope. So in your example, you have the following nesting of scopes:

global --> outer --> inner --> vnat
  |         |          |        |
  v         v          v        v
 x=0       x=1  <--nl--x <--nl--x       "nl" stands for "nonlocal"

So vnat refers to its enclosing scope's x (which is the scope of inner) which in turn refers to its enclosing scope's x (which is the scope of outer). For that reason, if you assign to x in vnat it will effectively assign to outer's name x.

a_guest
  • 34,165
  • 12
  • 64
  • 118