Nested functions in Python

Share
Copied to clipboard.
Trey Hunner smiling in a t-shirt against a yellow wall
Trey Hunner
4 min. read Watch as video Python 3.9—3.13

In Python, you can define a function within function.

Let's talk about nested in functions in Python.

A function defined within a function

Python's functions can be defined pretty much anywhere.

You can even define a function inside a function:

def greet_me(name="friend"):
    def greet():
        print("Hello", name)
    greet()

When we call this greet_me function, it defines a greet a function and then calls that function:

>>> greet_me()
Hello friend

Note that the inner function is allowed to use the name from the outer function.

A function returned from another function

Instead of calling our inner function, let's try returning it:

def greet_me(name="friend"):
    def greet():
        print("Hello", name)
    return greet

To use this function, we can assign the return value of greet_me to a variable:

>>> greet_trey = greet_me("Trey")
>>> greet_trey
<function greet_me.<locals>.greet at 0x...>

That variable points to a function.

To use this function, we can call it by putting parentheses after a reference to it:

>>> greet_trey()
Hello Trey

This works because functions are just objects in Python and like every object we can point variables to them and pass them around.

The enclosing scope

Note that the name variable in our outer function sticks around even after that function has returned.

Each time we call the greet_me function we dynamically define a new greet function:

>>> greet_carol = greet_me("Carol")
>>> greet_brett = greet_me("Brett")
>>> greet_carol()
Hello Carol
>>> greet_brett()
Hello Brett

Each greet function we make this way will have a separate name variable available to it, which is independent from the other greet functions we might have made the same way.

That name variable isn't a local variable to those inner functions, but it's also not a global variable. That name variable lives within the enclosing scope of those inner greet functions.

Closures with nested functions

When a function is defined within a function and the inner function accesses variables from the outer function, Python uses a "closure" to keep track of those variables that are defined just outside the inner function, in its enclosing scope.

You mostly need to think about a function's enclosing scope or the think about the closure.

Closures are something that happens behind the scenes and they pretty just much make nested functions work they way we would expect them to.

For example, here's another function that defines an inner function and returns it:

def make_adder(x):
    def add(y):
        return x + y
    return add

Each time we call the make_adder function it will return a new add function that has a different x variable available to it:

>>> add4 = make_adder(4)
>>> add5 = make_adder(5)
>>> add4(5)
9
>>> add5(5)
10

That word closure is most important for language implementers. For regular Python users we usually just think of the variables we're able to access rather than the fact that we've made a closure: those variables are accessible within our inner-most function even though they're neither local variables nor global variables.

Why nest functions within functions?

Why would we ever define a function inside another function?

Well, here's one use case:

def partial(func, *args, **kwargs):
    def new_func(*more_args, **more_kwargs):
        return func(*args, *more_args, **kwargs, **more_kwargs)
    return new_func

This partial function can sort of pre-load a future function call with specific arguments:

>>> hex_to_int = partial(int, base=16)

Now hex_to_int points to a function which will call int with the keyword argument base set to 16 as well as any additional given arguments:

>>> hex_to_int("CAFE")
51966

This is sometimes called "partially evaluating" a function.

This partial is a decent example of a function defined within another function, except the Python standard library already has an implementation of this function in the functools module:

>>> from functools import partial

This ability to define functions within functions isn't seen very often, except it's very commonly seen when using a specific Python feature: decorators.

Decorators involve nested functions

A decorator is a function that accepts a function and returns a new function that replaces the original function.

Decorators almost always involve involve nested functions:

def log_me(func):
    def wrapper(*args, **kwargs):
        print("Calling with", args, kwargs)
        return_value = func(*args, **kwargs)
        print("Returning", return_value)
        return return_value
    return wrapper

Nested functions are possible in Python

In fancy Computer Science terminology, functions that accept other functions as arguments or return a functions are often sometimes called "higher order functions".

It's possible in Python to dynamically define a function within another function and we can even return that inner function so that it can be called it later. Nested functions will automatically keep track of the variables that were available in their enclosing scope, that is the variables defined within their outer function.

Decorators are the most common use case for nested functions in Python.

A Python Tip Every Week

Need to fill-in gaps in your Python skills? I send weekly emails designed to do just that.