0

Today I'm reading python change log and meet the nonlocal keyword and did some experiment with it. I find a confusing situation where untriggered assignment will change the nonlocal keyword behavior, please see the example below.

def a():
    x = 'a'
    def b():
        def c():
            nonlocal x
            x = 'c'
        c()
    b()
    print(x)

a()

>>> python3 test.py
c


def a():
    x = 'a'
    def b():
        def c():
            nonlocal x
            x = 'c'
        c()
        if False:
            x = 'b'
    b()
    print(x)

a()

>>> python3 test2.py
a

You can saw that in test2.py, there is an untriggered assginment x = 'b' which changed the behavior of nonlocal.

Why this happened?

Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
Zen
  • 4,381
  • 5
  • 29
  • 56
  • 1
    Assignment statements always make a variable *local* unless you use `global` or `nonlocal`, it doesn't matter if the assignment is inaccesible at rutime. This happens at *compile time*. – juanpa.arrivillaga Aug 28 '18 at 08:29
  • @juanpa.arrivillaga, I know that, but I mean, by my understanding, when python interpreter reads `if False` and find out condition not satisfied, it won't execute the `x = 'b'` statement, then how would an not executed assignment influenced the nonlocal keyword? – Zen Aug 28 '18 at 08:33
  • because it happens at *compile time*, the runtime behavior is irrelevant. Read the linked duplicate. – juanpa.arrivillaga Aug 28 '18 at 08:33

1 Answers1

3

Python decides which variables are local to a function at compile time. x is assigned to within the function b, so it's local. That that branch is never actually reached at runtime is irrelevant and can't be decided in general.

So the x in c that is nonlocal is the next x in an outer scope, namely the one in b.

The alternative would be much more surprising -- consider what would happen if the if False: was instead if rand(10) == 6:. Then during the first call of b the nonlocal variable would refer to the outermost one, but randomly at some later call of b it would start referring to the other one!

RemcoGerlich
  • 30,470
  • 6
  • 61
  • 79