Clarifications
What is a Higher-Order Function?
Section titled “What is a Higher-Order Function?”A higher-order function is a function that does at least one of the following:
- Takes a function as an argument — it receives behaviour as input
- Returns a function as output — it produces new behaviour as output
The term comes from mathematics, where functions that operate on other functions are called higher-order. In programming, it is one of the foundational ideas of functional programming — treating functions as first-class values that can be passed around, stored, and returned just like any other data.
This is the idea that unlocks most of what makes functional programming powerful. map(), filter(), reduce(), closures, decorators, and functools.partial are all higher-order functions.
Functions as Arguments
Section titled “Functions as Arguments”The simplest form — passing a function into another function to customise its behaviour:
# apply() takes a function and a value — the function is an argumentdef apply(func, value): return func(value)
print(apply(str.upper, "hello")) # "HELLO"print(apply(len, "hello")) # 5print(apply(lambda x: x ** 2, 5)) # 25This is exactly what map() and filter() do — they accept a function and an iterable, and apply the function to the data:
nums = [1, 2, 3, 4, 5]
# map and filter are higher-order functionssquared = list(map(lambda x: x ** 2, nums)) # [1, 4, 9, 16, 25]evens = list(filter(lambda x: x % 2 == 0, nums)) # [2, 4]Functions as Return Values
Section titled “Functions as Return Values”A function that produces a new function — this is the pattern behind closures and function factories:
# make_multiplier returns a new function configured with a fixed factordef make_multiplier(factor): def multiply(x): return x * factor return multiply
double = make_multiplier(2)triple = make_multiplier(3)
print(double(5)) # 10print(triple(5)) # 15
# the returned functions work independentlynums = [1, 2, 3, 4, 5]print(list(map(double, nums))) # [2, 4, 6, 8, 10]print(list(map(triple, nums))) # [3, 6, 9, 12, 15]Both at Once — Functions In and Out
Section titled “Both at Once — Functions In and Out”The most powerful form — a function that takes a function and returns a modified version of it. This is the pattern behind decorators:
# timer takes a function and returns a new version that measures execution timeimport time
def timer(func): def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) end = time.time() print(f"{func.__name__} took {end - start:.4f}s") return result return wrapper
def slow_sum(n): return sum(range(n))
timed_sum = timer(slow_sum)print(timed_sum(1_000_000)) # slow_sum took 0.0312s # 499999500000Python’s @ decorator syntax is just a shorthand for this pattern:
@timer # equivalent to: slow_sum = timer(slow_sum)def slow_sum(n): return sum(range(n))A Practical Example — Building a Pipeline
Section titled “A Practical Example — Building a Pipeline”Higher-order functions compose naturally. Here is a pipeline that applies a list of transformations to data:
def apply_all(funcs, value): """Apply a list of functions in sequence to a value.""" result = value for func in funcs: result = func(result) return result
pipeline = [ str.strip, str.lower, lambda s: s.replace(" ", "_"),]
print(apply_all(pipeline, " Hello World ")) # "hello_world"print(apply_all(pipeline, " Python Tips ")) # "python_tips"Or more functionally, using reduce():
from functools import reduce
def pipe(value, *funcs): return reduce(lambda v, f: f(v), funcs, value)
print(pipe(" Hello World ", str.strip, str.lower, lambda s: s.replace(" ", "_")))# "hello_world"Summary
Section titled “Summary”| Pattern | Description | Example |
|---|---|---|
| Function as argument | Pass behaviour into a function | map(func, data) |
| Function as return value | Produce new behaviour | make_multiplier(2) |
| Both | Transform a function into another | Decorators, lru_cache |
The key insight is that functions are just values — they can be passed, returned, stored in lists, and used as dictionary values. Once you see functions this way, the door opens to a much more expressive and composable style of programming.