Search code examples
pythonscopeglobal-variables

How scope is determined in Python


Why does the first print statement in the second function throw an error that x is not defined?

x = 5

def function_a():
    print(x)


def function_b():
    print(x)
    x = 7
    print(x)

Running the first function gives the following result.

>>> function_a()
5

While running the second function throws an error.

UnboundLocalError: local variable 'x' referenced before assignment

Solution

  • Python will infer and use the variable from the inner scope whenever it sees the variable declared within the scope, even if that variable is declared after usage.

    To demonstrate this, I created two scenarios.


    Variable declared inside the inner scope

    A variable is inferred in the following order: local, nonlocal, and global. Since x is declared inside the inner scope, Python will infer and use the x in this scope even if it is declared after usage.

    Note that Python cannot distinguish modification from declaration; what was meant to modify x from the global scope was interpreted to declare another variable x within that scope.

    Inferred Inner

    No variables declared inside the inner scope

    If no variables are declared within the inner scope, Python will switch the inferred scope to nonlocal, and then to global.

    Inferred Global

    Explicitly switching scope

    If you explicitly declared the scope of x beforehand, Python would not have to infer.

    Declared Global

    The following code will not throw an error because the scope it uses is explicitly the global scope instead of the inner one.

    x = 5
    
    def scope():
        global x
        print(x)
        x = 7
        print(x)
    

    Scopes

    By choosing which scope to work with, you are not only using the variables within that specific scope but also modifying the variables. Therefore, you need extra caution when dealing with scopes.

    Because Python cannot distinguish variable declaration from variable modification, my advice is that if you want to use a global variable, you should explicitly state it beforehand.

    This also applies to nested scopes.

    x = 5
    
    def outer():
        x = 7
    
        def inner():
            nonlocal x
            print(x)
            x = 3
            print(x)
        
        inner()
    

    Running the outer function gives the following result.

    >>> outer()
    7
    3
    

    Try changing the nonlocal keyword to global and see a different result; or remove the line completely to get an error.