Higher-Order Functions in Python
Table of Contents
In Python, functions are just values, like strings, integers, or objects. A programming language "supports first-class functions" when functions are treated like any other variable. That means functions can be passed as arguments to other functions, can be returned by other functions, and can be assigned to variables. Functions that accept other functions as arguments or return functions are called higher-order functions, and they power built-ins like map(), filter(), and reduce().
All the content from our Boot.dev courses are available for free here on the blog. This one is the "First-Class and Higher-Order Functions" chapter of Learn Functional Programming in Python. If you want to try the far more immersive version of the course, do check it out!
What Are First-Class Functions in Python?
Because functions are just values, we can assign an existing function to a variable:
from collections.abc import Callable
def add(x: int, y: int) -> int:
return x + y
addition: Callable[[int, int], int] = add
print(addition(2, 5))
# 7
Callable is the type hint for a function. Callable[[int, int], int] means a function that takes two ints as arguments and returns an int. The new addition variable behaves the same as the original add function.
What Is a Higher-Order Function?
Here's a higher-order function that applies a given function to every item in a list:
from collections.abc import Callable
def square(x: int) -> int:
return x * x
def my_map(func: Callable[[int], int], arg_list: list[int]) -> list[int]:
result: list[int] = []
for i in arg_list:
result.append(func(i))
return result
squares: list[int] = my_map(square, [1, 2, 3, 4, 5])
print(squares)
# [1, 4, 9, 16, 25]
How Do Lambda (Anonymous) Functions Work?
Anonymous functions have no name, and in Python, they're called lambda functions after lambda calculus. Here's a lambda function that takes a single argument x and returns the result of x + 1:
lambda x: x + 1
Notice that the expression x + 1 is returned automatically, no need for a return statement. Compare that to how you'd normally write a function:
def add_one(x: int) -> int:
return x + 1
Because functions are values, you can assign a lambda to a variable:
from collections.abc import Callable
add_one: Callable[[int], int] = lambda x: x + 1
print(add_one(2))
# 3
Lambda functions might look scary, but they're still just functions. Because they simply return the result of an expression, they're often used for small, simple evaluations.
How Does the map() Function Work in Python?
"Map," "filter," and "reduce" are three commonly used higher-order functions in functional programming.
In Python, the built-in map() function takes a function and an iterable (often a list) as inputs. It returns an iterator that applies the function to every item, yielding the results. With map, we can operate on lists without using loops and nasty stateful variables. For example, given this code:
def square(x: int) -> int:
return x * x
nums: list[int] = [1, 2, 3, 4, 5]
squared_nums: list[int] = []
for num in nums:
num_squared: int = square(num)
squared_nums.append(num_squared)
print(squared_nums)
# [1, 4, 9, 16, 25]
We could use map instead, like this:
from collections.abc import Iterator
def square(x: int) -> int:
return x * x
nums: list[int] = [1, 2, 3, 4, 5]
squared_nums: Iterator[int] = map(square, nums)
print(list(squared_nums))
# [1, 4, 9, 16, 25]
map() returns a "map object," so the list() type constructor is needed to convert it back into a standard list.
How Does the filter() Function Work?
The built-in filter() function takes a function and an iterable (often a list) and returns an iterator that keeps elements from the original iterable only where the result of the function on that item returned True:
def is_even(x: int) -> bool:
return x % 2 == 0
numbers: list[int] = [1, 2, 3, 4, 5, 6]
evens: list[int] = list(filter(is_even, numbers))
print(evens)
# [2, 4, 6]
What Does reduce() Do in Python?
The built-in functools.reduce() function takes a function and a list of values, and applies the function to each value in the list, accumulating a single result as it goes:
import functools
def add(sum_so_far: int, x: int) -> int:
return sum_so_far + x
numbers: list[int] = [1, 2, 3, 4]
sum: int = functools.reduce(add, numbers)
print(sum)
# 10
Notice that we're passing the function add without the ()! It means that reduce will take care of execution and pass the parameters for you. Think of passing add like handing someone a recipe (the instructions), instead of the finished dish (the result of the execution).
Map, Filter, and Reduce vs. Loops
Higher-order functions like map, filter, and reduce allow us to avoid stateful iteration and mutation of variables. Take a look at this imperative code that calculates the factorial of a number:
def factorial(n: int) -> int:
result: int = 1
for i in range(1, n + 1):
result *= i
return result
Here's the same factorial function using reduce:
import functools
def factorial(n: int) -> int:
return functools.reduce(lambda x, y: x * y, range(1, n + 1))
In the functional example, we're just combining functions to get the result we want. There's no need to reassign variables or keep track of the program's state in a loop.
A loop is inherently stateful! Depending on which iteration you're on, the i variable has a different value.
How Does the zip() Function Work?
The zip() function takes two iterables (often lists), and returns a new iterable where each element is a tuple containing one element from each of the original iterables:
a: list[int] = [1, 2, 3]
b: list[int] = [4, 5, 6]
c: list[tuple[int, int]] = list(zip(a, b))
print(c)
# [(1, 4), (2, 5), (3, 6)]
zip pairs well with filter: you can zip two lists into tuples and then filter the resulting tuples. For more, see our functional programming guide, the different types of functions, Python functions, and the full Learn Functional Programming in Python course.
Frequently Asked Questions
What is a higher-order function in Python?
A higher-order function is a function that either accepts another function as an argument or returns a function. Built-ins like map, filter, and reduce are common examples because they take a function as one of their inputs.
What is the difference between a first-class function and a higher-order function?
A first-class function is a function treated like any other value, meaning it can be assigned to variables, passed around, and returned. A higher-order function is one that actually takes a function as an argument or returns a function. First-class functions are what make higher-order functions possible.
What is a lambda function in Python?
A lambda is an anonymous function with no name. It returns the result of a single expression automatically, without a return statement. Lambdas are useful for small, simple operations, especially when passed to map, filter, or reduce.
Why use map and filter instead of a for loop?
map and filter let you transform and select items from an iterable without manually managing a loop or a mutable accumulator variable. This avoids stateful iteration and mutation of variables.
Where does the reduce function live in Python?
reduce is not a built-in. It lives in the functools module of the standard library, so you import it with import functools and call it as functools.reduce.
