Higher-Order Functions
A higher-order function is a function that does at least one of the following: it takes one or more functions as arguments, or it returns a function as its result. Many of Python's most useful built-ins and patterns — sorted(), map(), filter(), decorators, callbacks — are higher-order functions under the hood. Understanding them unlocks a much more flexible, expressive style of Python.
Functions Are First-Class Citizens
Higher-order functions are only possible because Python treats functions as first-class objects. That means a function is just another value: it can be assigned to a variable, stored inside a list or dictionary, passed as an argument, and returned from another function — exactly like an integer or a string.
def greet(name):
return f"Hello, {name}!"
# Assign a function to a variable (no parentheses = don't call it, just reference it)
say_hello = greet
print(say_hello("Ada")) # Hello, Ada!
# Store functions in a list, just like any other value
operations = [str.upper, str.lower, str.title]
for op in operations:
print(op("python is fun"))Passing Functions as Arguments
The most common higher-order pattern is the callback: a function that accepts another function as a parameter and calls it at the appropriate moment. This lets you separate "what to loop over / when to run" from "what to actually do."
def apply_twice(func, value):
"""Call func on value, then call func again on the result."""
return func(func(value))
def increment(x):
return x + 1
def square(x):
return x * x
print(apply_twice(increment, 5)) # 7 -> (5 + 1) + 1
print(apply_twice(square, 3)) # 81 -> (3 * 3) ** 2
# Works just as well with an anonymous function
print(apply_twice(lambda x: x * 10, 2)) # 200This is exactly the pattern behind event handling, sorting keys, and test frameworks: some general-purpose code calls "your" function at the right time, without needing to know what that function actually does.
Returning Functions: The Factory Pattern
A function can also build and return another function. This "factory" pattern is useful when you want to generate a family of related functions that each remember some configuration.
def make_multiplier(n):
"""Return a new function that multiplies its input by n."""
def multiplier(x):
return x * n
return multiplier
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
print(double(21)) # 42Higher-Order Functions in the Wild: key=
You don't need to write your own higher-order functions to benefit from them — Python's standard library leans on the pattern constantly. sorted(), max(), and min() all accept a key parameter: a function that is called on each element to decide how it should be ranked.
students = [
{"name": "Ada", "score": 92},
{"name": "Grace", "score": 98},
{"name": "Alan", "score": 85},
]
# Sort by score, highest last (default ascending)
by_score = sorted(students, key=lambda s: s["score"])
print([s["name"] for s in by_score]) # ['Alan', 'Ada', 'Grace']
# Sort by name length
by_name_length = sorted(students, key=lambda s: len(s["name"]))
print([s["name"] for s in by_name_length]) # ['Ada', 'Alan', 'Grace']# Tuples work just as well as dicts
inventory = [("apples", 12), ("bananas", 45), ("cherries", 3)]
# Find the item with the most stock
most_stocked = max(inventory, key=lambda item: item[1])
print(most_stocked) # ('bananas', 45)
# Find the item with the least stock
least_stocked = min(inventory, key=lambda item: item[1])
print(least_stocked) # ('cherries', 3)In every one of these calls, key is a function passed as an argument — sorted, max, and min are themselves higher-order functions, and they don't care whether you hand them a lambda, a named function, or something like str.lower.
A higher-order function takes a function as an argument, returns a function, or both.
This only works because functions are first-class values in Python.
The "callback" pattern passes a function in so general-purpose code can call it later.
The "factory" pattern returns a function, usually relying on a closure to remember configuration.
sorted(), max(), and min() accept a key= function as an everyday, practical example.