Scope
A variable created inside a function only exists while that function runs — but a function can still read variables from outside it.
By the end of this lesson, you can
- Explain why a variable created inside a function isn't visible outside it
- Predict a NameError caused by using a name outside the scope where it was created
- Explain why a function can read a variable from the surrounding scope, even without a parameter
Why it matters
Every function written so far has used variable names freely inside its
own body without worrying whether those names existed anywhere else.
That freedom comes from scope: the region of a program where a
particular name is visible. Understanding it explains a specific,
common error — using a name that “should” exist and getting a
NameError — and clarifies exactly what a function can and can’t see.
Mental model
A variable created inside a function is local to it: it comes into existence when the function is called, and stops existing the moment the function returns. It was never visible outside the function to begin with — not because it was deleted, but because it never had meaning anywhere else.
def set_total():
total = 100
print("Inside:", total)
set_total()
print("Outside:", total)Inside: 100
Traceback (most recent call last):
File "example.py", line 6, in <module>
print("Outside:", total)
NameError: name 'total' is not definedtotal prints fine from inside set_total — but by the time control
returns to the last line, total was never in scope there at all.
Trace it
| Line | total visible here? |
|---|---|
| inside set_total(), after total = 100 | Yes — local to this call |
| after set_total() returns | No — total no longer exists |
| print("Outside:", total) | No — raises NameError |
A function can still read outer variables
DISCOUNT_RATE = 0.1
def apply_discount(price):
return price * (1 - DISCOUNT_RATE)
print(apply_discount(100))90.0apply_discount never received DISCOUNT_RATE as a parameter, yet it
reads it without any error. A function can see and read any variable
that already existed in the surrounding scope when it runs — the rule
from the first example only applies to variables created inside the
function.
Check your understanding
Or reveal the answer without checking
Answer:Raises a NameError
message is local to greet — it exists only during that call, even though the function returned it. The returned value was never captured (greet()'s result was discarded, not assigned to anything outside), so message still doesn't exist outside the function.
Or reveal the answer without checking
Answer:Yes — a function can read any variable that already exists in the surrounding scope
Reading an outer-scope variable from inside a function works without any special syntax — the restriction is only on variables created inside the function, which don't exist outside it.
Practice: warm-up
Trace this program — for each print line, decide whether it succeeds
and what it prints, or whether it raises a NameError.
LIMIT = 10
def check(value):
result = value <= LIMIT
return result
print(LIMIT)
print(check(5))
print(result)
Stuck? Reveal one hint at a time.
Hint 1
LIMIT is defined at the top level, outside any function — where is it visible from?
Hint 2
result is created inside check — does it exist anywhere once check has returned?
Reveal the trace
Try the problem yourself before reading this. There is often more than one reasonable approach — treat this as one worked example, not the only correct answer.
print(LIMIT) -> prints 10 (LIMIT is at the top level, visible everywhere)
print(check(5)) -> prints True (5 <= 10)
print(result) -> NameError — result only ever existed inside check()Practice: apply it
This function is meant to report a running total, but the caller can never see it:
def add_to_total(previous_total, amount):
total = previous_total + amount
return total
add_to_total(0, 10)
print(total)
Or reveal the answer without checking
Answer:total is local to add_to_total and never existed outside it, so print(total) raises a NameError, even though the function itself ran without any problem
add_to_total(0, 10) runs fine and returns 10 — but that return value was never captured anywhere. total is local to the function, so print(total) has nothing to refer to outside it.
Modification challenge: fix this by keeping the running total
outside the function, in a variable the function’s return value gets
assigned back into — for example, total = add_to_total(total, 10),
with add_to_total taking the current total as a parameter and
returning the new one.
Summary
- A variable created inside a function is local to it — it exists only while that call runs, and using its name outside the function raises a
NameError. - A function can freely read a variable that already exists in the surrounding scope, without needing it as a parameter.
- Reassigning an outer variable from inside a function isn’t as simple as reading one — that’s a sharper edge case beyond this lesson.
- When a value needs to escape a function,
returnit and capture it in a variable outside — that’s the reliable way to make it visible where it’s needed.