Scope: Where Variables Live
About 15 minutes
Why this matters
Variables created inside a function are invisible outside it. This is a feature: two functions can both use a variable called total without touching each other. Understanding scope also explains one of Python’s most common surprises: functions can silently change the lists you give them.
The idea
def make_message():
message = "hi" # local: exists only inside this function
return message
make_message()
print(message) # NameError: message is not defined
A function can read a variable defined outside it:
TAX_RATE = 0.2 # a "constant", written in CAPITALS by convention
def with_tax(price):
return price * (1 + TAX_RATE)
But assigning to a name inside a function creates a new local variable; it does not change the outside one. (The global keyword can change that, but it makes code hard to follow. Prefer parameters and return.)
Lists and dicts are different. Calling .append() on a list a function received changes the caller’s list too, because both names point at the same list:
def add_one(items):
items.append(1) # changes the caller's list! (a "side effect")
mine = []
add_one(mine)
print(mine) # [1]
When a function should not change its input, build a new list and return it.
Try it
Write a function that tries to print a variable that only exists in another function. Read the NameError. Then call add_one on the same list twice and watch it grow.
Your turn
A file starts with TAX_RATE = 0.2. Write two functions:
add_tax(price): the price plus tax, rounded to 2 decimal places, usingTAX_RATE.add_tax(10)is12.0. (The checks changeTAX_RATE, so read it inside the function.)with_item(cart, item): a new list that iscartwithitemon the end. The originalcartmust stay unchanged.
Recap
- Names created inside a function are local; functions can read outer names but assigning makes a new local.
- Mutating a list or dict argument changes the caller’s data: avoid it unless that is the point.
- Return new values instead of changing inputs.
Common mistakes
UnboundLocalError: assigning to a name inside a function makes it local for the whole function, so reading it earlier fails.- Surprise side effects from
.append(),.sort()ordelon an argument. - Overusing
global, which hides where values change.