Python Nested Functions
By Flavio Copes
Learn how nested functions work in Python: defining a function inside another to hide local helpers, and using nonlocal to reach the outer variables.
Functions in Python can be nested inside other functions. A function defined inside a function is visible only inside that function.
This is useful to create utilities that are useful to a function, but not useful outside of it.
You might ask: why should I be “hiding” this function, if it does not harm?
One, because it’s always best to hide functionality that’s local to a function, and not useful elsewhere. The rest of the program can’t call it by accident, and its name doesn’t pollute the module.
Also, because we can make use of closures, which I’ll show you below.
Here is an example. say() only exists inside talk():
def talk(phrase):
def say(word):
print(word)
words = phrase.split(' ')
for word in words:
say(word)
talk('I am going to buy the milk')
This prints each word on its own line. Calling say('hello') outside of talk() raises a NameError, because the name is not defined there.
Accessing outer variables with nonlocal
The inner function can read variables from the outer function without any special syntax. But if you want to assign to a variable defined in the outer function, you first need to declare it as nonlocal:
def count():
count = 0
def increment():
nonlocal count
count = count + 1
print(count)
increment()
count()
# 1
What happens if you forget nonlocal?
You get an UnboundLocalError. When Python sees an assignment inside a function, it treats that name as local to it. So count = count + 1 tries to read a local variable that has no value yet:
def count():
count = 0
def increment():
count = count + 1 # UnboundLocalError
increment()
The fix is the nonlocal count declaration you saw above. Remember: reading works out of the box, assigning requires nonlocal.
Closures
The interesting part is that the inner function keeps access to the outer variables even after the outer function has returned. That’s a closure.
Here we return the inner function, and it remembers its own count:
def make_counter():
count = 0
def increment():
nonlocal count
count = count + 1
return count
return increment
counter = make_counter()
print(counter()) # 1
print(counter()) # 2
print(counter()) # 3
make_counter() finished running long ago, but counter() still updates the count variable it closed over. Each call to make_counter() creates a fresh, independent counter.
Related posts about python: