Variable Scope
Where a variable lives and who can see it
In this lesson
Explain it like I’m 5
Each function has its own private notebook. Things it jots down disappear when it finishes, and other functions can’t read its notebook. To share, you pass notes in and hand results back.
Local scope: private by default
A variable assigned inside a function is local: it exists only while the function runs and can’t be seen from outside. Two functions can each have a variable called total without interfering, because each total lives in its own scope. When the function returns, its local variables are gone.
def make_total():
total = 5 # local to this function
total = total + 1
return total
result = make_total()
print(result)
print(total) # error: total doesn't exist out here
6 NameError: name 'total' is not defined
Global scope and reading outer variables
Variables defined at the top level of your file are global. A function can read a global variable, but if it assigns to a name, Python treats that name as local to the function. This is why a function can use a global constant but can’t accidentally overwrite your top-level variables just by assigning.
TAX_RATE = 0.2 # a global constant
def with_tax(price):
return price + price * TAX_RATE # reads the global
print(with_tax(100))
120.0
The clean pattern: pass in, return out
Rather than relying on globals, the reliable approach is to pass values in as arguments and return results out. This keeps each function self-contained and easy to test: its behaviour depends only on its inputs, not on hidden outside state. Reach for globals sparingly — usually only for constants.
This counter doesn't accumulate because each call resets the local total. Fix it by passing the running total in and returning the new one.
def add_points(points):
total = 0
total = total + points
return total
score = add_points(10)
score = add_points(5)
print(score)
Pass the current score in as an argument and return current + points, then reassign score at the top level.
def add_points(current, points):
return current + points
score = 0
score = add_points(score, 10)
score = add_points(score, 5)
print(score)
15
The global keyword (and why to avoid it)
If you truly must reassign a global from inside a function, the global keyword allows it. But code that changes globals is harder to follow and debug, because any function might alter shared state. Prefer returning a new value and reassigning at the top level instead.
The LEGB rule: where Python looks for a name
When you use a name, Python searches four scopes in a fixed order — Local (inside the current function), Enclosing (an outer function wrapping this one), Global (the top level of the file), and Built-in (names like print and len). It uses the first match it finds.
That order — ‘LEGB’ — explains why a local variable can ‘shadow’ a global of the same name, and why print works everywhere: it lives in the built-in scope, the last place Python looks.
x = "global"
def outer():
x = "enclosing"
def inner():
print(x) # no local x, so Python looks outward
inner()
outer()
print(x) # the global x is untouched
enclosing global
Common mistake: Expecting a function to change an outer variable by assigning a local
Assigning x = ... inside a function creates a new local x; it doesn’t touch the global of the same name.
Return the new value and reassign it outside: x = update(x). Avoid relying on functions to mutate outer variables.
A variable assigned inside a function is…
Assigning inside a function creates a local variable that vanishes when the function returns.
What's the recommended way to get data into and out of a function?
Passing arguments in and returning results keeps functions self-contained and testable.
Mini exercise (medium)
Without using global, write increment(value) that returns value + 1. Use it to take a top-level count = 0 up to 3 by reassigning.
Do it here. Finish the code below and press Run to try your answer right in the browser — no setup needed.
def increment(value):
# return value + 1 (no global!)
return value
count = 0
# TODO: call increment three times, reassigning count each time
print(count)
Reassign at the top level: count = increment(count), three times.
def increment(value):
return value + 1
count = 0
count = increment(count)
count = increment(count)
count = increment(count)
print(count)
3
assert increment(0) == 1, "increment should return value + 1"
assert count == 3, "reassign count three times"
print("✓ Correct!")