How scope is determined in Python

Question:

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
Asked By: quickhaze

||

Answers:

In the 2nd method, you have written x = 7, which makes x a local variable for that method. But since, you are trying to access it on the print statement before the line "x = 7", it throws an error saying local variable x is accessed before assignment.

If you remove the line x = 7, it would work just fine.
You can try this

x = 5

def vs_code():
    print(x)

def vs_code1():
    print(x)
    y = 7
    print(y)

this will print
5
5
7

Now, since I am not declaring x inside 2nd method, now x is not local for 2nd method. If you declare it inside 2nd method, it will interpret that as a local variable. and you have to use it only after assignment.

Hope you understood.

Answered By: Gokul Garre

python is special in variable scope

you can use dis module to see what happeded.

def foo():
    print(x)
    print(y)
    x = ...
import dis
dis.dis(foo)

output

  2           0 LOAD_GLOBAL              0 (print)
              2 LOAD_FAST                0 (x)
              4 CALL_FUNCTION            1
              6 POP_TOP

  3           8 LOAD_GLOBAL              0 (print)
             10 LOAD_GLOBAL              1 (y)
             12 CALL_FUNCTION            1
             14 POP_TOP

  4          16 LOAD_CONST               1 (Ellipsis)
             18 STORE_FAST               0 (x)
             20 LOAD_CONST               0 (None)
             22 RETURN_VALUE

x is LOAD_FAST

y is LOAD_GLOBAL
before running, python thinks x is a local variable because you try to modify it (cannot modify global variable).

this will works fine

def vs_code1():
    global x
    print(x)
    x = 7
    print(x)
Answered By: ianfun

When a variable is assigned a value inside a function, that variable name is considered local. Unless it is explicitly declared as global with the line:

global x

In the first print of the function the variable x is not defined in the local scope. That-s why throws an error.

So you have to add the line global x at the beggining of the function body for the variable x be considered global despite it is assigned in the function.

Answered By: nadapez

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.

Answered By: Troll
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.