FunctionsChapter 53 of 114
Scope
Where a name is visible, and the order Python searches.
Local names
A name assigned inside a function belongs to that function and disappears when it returns:
def work():
total = 10
print("inside:", total)
work()
try:
print(total)
except NameError as problem:
print("NameError:", problem)Output
inside: 10 NameError: name 'total' is not defined
The search order
When Python meets a name it looks in four places, in order:
- Local — this function
- Enclosing — any function wrapped around it
- Global — the module
- Built-in —
print,lenand friends
name = "global"
def outer():
name = "enclosing"
def inner():
print(name)
inner()
outer()
print(name)Output
enclosing global
inner has no name of its own, so it found the enclosing one before the global.
Assignment makes a name local
The decision is made for the whole function, before it runs, so a name assigned anywhere in the body is local everywhere in it:
count = 10
def show():
print(count)
show()Output
10
Add an assignment and the same read breaks:
count = 10
def bump():
try:
print(count)
count = count + 1
except UnboundLocalError as problem:
print("UnboundLocalError:", problem)
bump()Output
UnboundLocalError: cannot access local variable 'count' where it is not associated with a value
Nothing about the print line changed. The assignment below it made count local for the whole function.
global and nonlocal
global rebinds a module-level name:
count = 0
def bump():
global count
count += 1
bump()
print(count)Output
1
nonlocal rebinds a name in the enclosing function, which is what you need for a counter that lives in a closure:
def make_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
counter = make_counter()
print(counter(), counter(), counter())Output
1 2 3
Without nonlocal, count += 1 would try to make a local and raise UnboundLocalError.
Neither is needed to mutate
Both keywords are about rebinding a name. Changing an object in place needs no declaration:
scores = []
def record(value):
scores.append(value)
record(1)
print(scores)Output
[1]
Blocks are not scopes
Unlike many languages, if, for and while do not create a scope. A name assigned inside one survives:
for i in range(3):
last = i
print(i, last)Output
2 2
Only functions, classes, modules and comprehensions do.
Shadowing builtins
A local name can hide a builtin for the rest of that scope:
def total(numbers):
sum = 0
for n in numbers:
sum += n
return sum
print(total([1, 2, 3]))Output
6
That works, and inside total the real sum is gone. Name it running_total and the problem never arises.
Test yourself
3 questionsIn what order does Python look up a name?
Show the answer
Local, enclosing, global, built-in — The first three letters spell LEGB, which is how most people remember it.
Why can reading a global fail with UnboundLocalError?
Show the answer
An assignment anywhere in the function makes the name local for the whole function — The decision is made before the function runs, so a later assignment affects an earlier read.
Which keyword rebinds a name in an enclosing function?
Show the answer
nonlocal — global reaches the module level; nonlocal reaches the function one level out, which is what closures need.
Recursion
A function that calls itself, and the base case that stops it.