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)Each call gets a fresh set of local variables, and they disappear when the function returns.
Parameters are local variables too:
def double(n):
n = n * 2
return n
number = 5
print(double(number))
print(number) # unchanged: the function changed its own local nGlobal 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))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():
count = 0 # a NEW local variable
print("inside:", count)
reset()
print("outside:", count) # the global is untouchedThis 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()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)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)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()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())The inner function "remembers" count even after make_counter has finished. This is called a closure, and you'll see it again with decorators.
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()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] sum = 0 # hides the built-in sum() print(sum(numbers))
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)Assigning to x inside the function creates a new local variable, separate from the global x.
The global x is never changed, so think about what the last line prints.
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)Because score is assigned inside the function, Python treats it as local, so reading it first fails.
Add global score as the first line inside the function.
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)Give the function a score parameter and return the new score.
Call it as score = add_points(score, 5) so the result is stored.
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))total belongs to make_accumulator, so the inner function must say nonlocal total before changing it.
Then add n to total and return total.
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 namelets a function change a global variable. Prefer passing values in and returning results.nonlocal namelets 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.