PythonHigher-Order Functions

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.

Python
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"))
Note
This "functions as values" idea is the foundation everything below builds on. If a function couldn't be passed around like a normal value, it could never be handed to another function or handed back out of one.
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."

Python
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))  # 200

This 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.

Python
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))  # 42
Tip
Notice how `multiplier` still "remembers" the value of `n` from `make_multiplier` even after `make_multiplier` has finished running. This is a **closure** — the returned function closes over the variables from its enclosing scope. Closures are covered in depth on their own page; the key takeaway here is that returning functions and closures go hand in hand.
Higher-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.

Python
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']

Python
# 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.

Note
Higher-order functions show up again, in more specialized forms, on the `map()` / `filter()` / `reduce()` page (functions that transform or reduce sequences) and on the decorators page (functions that wrap other functions to extend their behavior). Once this pattern clicks, you'll start noticing it everywhere in Python.