Lesson 5 of 10
Scope is the region of code where a variable exists. Variables created inside a function are local to it — they are born when the function runs and vanish when it returns.
def calculate():
result = 42
print(result)
calculate()
print(result)
That last line raises NameError: name 'result' is not defined. result only ever existed
inside calculate.
This is a feature, not a limitation. It means two functions can both use a variable called
total without interfering with each other — you never have to worry about what names another
function chose.
Global scope
Variables defined outside any function are global and can be read anywhere:
tax_rate = 0.1
def total(price):
return price + (price * tax_rate)
print(total(100))
Reading versus assigning
Here is the part that surprises people. Reading a global works fine. Assigning to that name inside a function creates a new local variable instead:
count = 0
def increment():
count = count + 1
increment()
That raises UnboundLocalError. Because count is assigned somewhere in the function, Python
treats it as local throughout the function — so count + 1 tries to read a local that has no
value yet.
The global keyword
global tells Python you really do mean the outer variable:
count = 0
def increment():
global count
count = count + 1
increment()
increment()
print(count)
This works, but use it sparingly. A function that quietly modifies global state is hard to test and hard to reason about — you can't tell what it does by looking at its inputs and output.
Almost always, the better shape is to take a value in and return a new one:
def increment(count):
return count + 1
count = 0
count = increment(count)
count = increment(count)
print(count)
Same result, no hidden dependency. The function is now completely predictable.
Parameters are local too
Reassigning a parameter inside a function does not affect the caller's variable:
def change(value):
value = 99
return value
original = 1
print(change(original))
print(original)
Careful though — this holds for immutable values like numbers and strings. If you pass a
list and mutate it with .append(), the caller does see the change, because both names
point at the same list.
def add_item(items):
items.append("new")
basket = ["apple"]
add_item(basket)
print(basket)
Try it yourself
Show that a local variable doesn't leak, and that a global can be read.
Checkpoint
- Variables made inside a function are local and disappear when it returns.
- Globals can be read inside functions but not reassigned without
global. - Prefer parameters and return values over
global. - Mutating a list argument is visible to the caller; reassigning a parameter is not.
Questions about this lesson
Log in to ask a question.