Know some Python? Write your own interactive tutorial, with runnable examples and exercises, and share it with a link. Write and share your own tutorial.

Start writing

Learn Python / Scope

Scope

Where a variable is created decides where it can be used. That's called its scope. Understanding scope explains some confusing errors, and it helps you write functions that don't accidentally interfere with each other.

Local variables

A variable created inside a function is local to that function. It exists only while the function runs, and code outside can't see it:

def make_greeting():
    message = "Hello from inside!"
    print(message)

make_greeting()
print(message)
Output

The last line fails with a NameError: message only existed while make_greeting() was running. Each call gets a fresh set of local variables, and they disappear when the function returns. That's a good thing: two functions can both use a variable called total without interfering with each other.

Parameters are local variables too:

def double(n):
    n = n * 2
    return n

number = 5
print(double(number))

# unchanged: only the local n changed
print(number)
Output

When you call double(number), the parameter n starts out referring to the same value as number. n = n * 2 then points n at a new value, 10. That reassignment only affects the local name n; number still refers to 5.

Lists behave differently in one way. If a function changes a list it was given (with append, for example) rather than assigning a new value to the parameter, the caller sees the change, because both names refer to the same list:

def add_bonus(scores):
    scores.append(100)

my_scores = [80, 90]
add_bonus(my_scores)
print(my_scores)
Output

Global variables

A variable created at the top level of your program, outside any function, is global. Functions can read global variables:

tax_rate = 0.2

def price_with_tax(price):
    return price * (1 + tax_rate)

print(price_with_tax(100))
Output

Same name, different variables

If a function assigns to a name, Python treats that name as local throughout the function, even if a global variable has the same name. The two are completely separate:

count = 10

def reset():
    # a NEW local variable
    count = 0
    print("inside:", count)

reset()

# the global is untouched
print("outside:", count)
Output

This leads to a classic error. Because count is assigned inside the function, Python treats it as local everywhere in that function. So reading it before the assignment fails:

count = 10

def increment():
    count = count + 1
    return count

increment()
Output

The error is an UnboundLocalError: "cannot access local variable 'count'". Python decides which names are local before the function runs, by looking for assignments. It sees count = ..., so count is local everywhere in increment. When it then evaluates the right side, count + 1, the local count has no value yet. It never falls back to the global one.

The global keyword

To change a global variable from inside a function, declare it with global:

count = 0

def increment():
    global count
    count += 1

increment()
increment()
print(count)
Output

global count tells Python "count in this function means the global variable", so count += 1 changes the variable outside. Without that line, you'd get the UnboundLocalError from above.

You only need global to assign a new value to a global name. Changing a global list in place, with append for example, works without it, because you're not assigning to the name:

log = []

def record(event):
    log.append(event)

record("start")
record("stop")
print(log)
Output

Use global sparingly. Functions that quietly change global variables are hard to follow and hard to test. It's usually cleaner to pass values in and return the result:

def increment(count):
    return count + 1

count = 0
count = increment(count)
count = increment(count)
print(count)
Output

Nested functions and nonlocal

Functions can be defined inside other functions. The inner function can read the outer function's variables:

def outer():
    message = "from outer"

    def inner():
        print("inner sees:", message)

    inner()

outer()
Output

To change a variable of the enclosing function, use nonlocal:

def make_counter():
    count = 0

    def increment():
        nonlocal count
        count += 1
        return count

    return increment

counter = make_counter()
print(counter())
print(counter())
print(counter())
Output

make_counter() runs once: it creates count = 0, defines increment and returns the function itself (no parentheses, so it isn't called). counter now refers to that inner function. Each counter() call adds 1 to the same count, giving 1, 2, 3.

The inner function "remembers" count even after make_counter has finished. This is called a closure, and you'll see it again with decorators. nonlocal works like global, but for the enclosing function's variable instead of a global one.

The LEGB rule

When Python looks up a name, it searches four scopes in order and uses the first match:

Order Scope What it is
1 Local Inside the current function
2 Enclosing Inside any outer function
3 Global The top level of the program
4 Built-in Python's own names, such as print and len
x = "global"

def outer():
    x = "enclosing"

    def inner():
        x = "local"
        print(x)

    inner()

outer()
Output

Delete the x = "local" line and run again, then delete x = "enclosing" too. Python finds the next x along the chain each time.

Don't shadow built-ins

Because built-ins come last in the search, a variable named list, sum or max hides the built-in function:

numbers = [3, 1, 2]

# hides the built-in sum()
sum = 0
print(sum(numbers))
Output

The error is a TypeError: "'int' object is not callable". After sum = 0, the name sum finds your 0 first (global comes before built-in), and you can't call a number.

Pick names like total or values instead.

Exercises

Exercise 1: Predict the output

Predict what each print shows, then run it to check.

x = 5

def change():
    x = 99
    print("inside:", x)

change()
print("outside:", x)
Output

Exercise 2: Fix the UnboundLocalError

This raises an error. Fix it with the global keyword so the program prints Score: 15.

score = 10

def add_points(points):
    score = score + points

add_points(5)
print("Score:", score)
Output

Exercise 3: Without global

Rewrite the previous program without global: make add_points take the score as a parameter and return the new score.

score = 10

def add_points(points):
    score = score + points

add_points(5)
print("Score:", score)
Output

Exercise 4: Running total

Complete make_accumulator() so it returns a function that adds each number it receives to a running total and returns the total. The calls should print 10, 15 and 35.

def make_accumulator():
    total = 0

    def add(n):
        # your code here
        pass

    return add

acc = make_accumulator()
print(acc(10))
print(acc(5))
print(acc(20))
Output

Summary

  • Variables created inside a function are local: they exist only during that call.
  • Top-level variables are global. Functions can read them freely.
  • Assigning to a name inside a function makes it local, which can cause an UnboundLocalError.
  • global name lets a function change a global variable. Prefer passing values in and returning results.
  • nonlocal name lets an inner function change a variable of its enclosing function.
  • Python looks names up in the order Local, Enclosing, Global, Built-in (LEGB). Avoid naming variables after built-ins.

Share this tutorial

Know something worth teaching? Write your own tutorial with runnable examples and share it.