2

If I run this script:

def main_function():
    b = 'b'

    def nested_function():
        print(b)
        #b = 'value change'

    nested_function()

main_function()

All is ok.

If I run this another script:

def main_function():
    b = 'b'

    def nested_function():
        #print(b)
        b = 'value change'

    nested_function()

main_function()

All is ok too. As you can see, here I change the line inside in the nested_function().

But if I use the two lines print(b) and b='value change' in the nested_function(). Like this:

def main_function():
    b = 'b'

    def nested_function():
        print(b)
        b = 'value change'

    nested_function()

main_function()

Then, there is an error. The error says:

UnboundLocalError: local variable 'b' referenced before assignment

I can't understand the logic of why this happens.

  • 1
    If a function contains an assignment to a variable, that variable is local by default (unless overridden by a `global` or `nonlocal` declaration). That is the entirety of Python's rules on variable scope. – jasonharper Jun 04 '22 at 00:39

1 Answers1

2

Let's break these down one-by-one. Python's scoping rules are a bit unhinged sometimes. The basic idea is that if you ever assign to a variable inside of a given function, then Python assumes that that variable is local to that particular function.

def main_function():
    b = 'b'

    def nested_function():
        print(b)

    nested_function()

main_function()

Here, you never assign to b inside of nested_function, so Python sees no need to create a new variable. Instead, it inherits the one from its enclosing scope.

def main_function():
    b = 'b'

    def nested_function():
        b = 'value change'

    nested_function()

main_function()

Here, you do assign to it, so you get a new variable. The b you assign to inside of nested_function is unrelated to the one inside of main_function. They just happen to share a name.

def main_function():
    b = 'b'

    def nested_function():
        print(b)
        b = 'value change'

    nested_function()

main_function()

Here, you do assign, so Python makes a new variable inside of the function, and every reference to b inside the nested function refers to that new variable, so it's as if you did this.

def main_function():
    b_0 = 'b'

    def nested_function():
        print(b_1)
        b_1 = 'value change'

    nested_function()

main_function()

So you're trying to print out b_1 before any value has been assigned to it, hence the error.

If b was a module-level variable, you would use the global keyword to mark it as such inside the function. In your case, it's a local variable; it's just not local to the immediately enclosing function. So we use the nonlocal keyword.

def main_function():
    b = 'b'

    def nested_function():
        nonlocal b
        print(b)
        b = 'value change'

    nested_function()

main_function()

nonlocal b says "I know I'm assigning to b, but I actually mean to use the one from the enclosing scope".

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116