In Python, you can define a function within function.
Let's talk about nested in functions in Python.
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.
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.
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.
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 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.
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
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.
Need to fill-in gaps in your Python skills?
Sign up for my Python newsletter where I share one of my favorite Python tips every week.
Need to fill-in gaps in your Python skills? I send weekly emails designed to do just that.
Sign in to your Python Morsels account to track your progress.
Don't have an account yet? Sign up here.