In Python, functions are "first-class citizens," meaning they can be assigned to variables, passed as arguments, and returned from other functions. A Higher-Order Function (HOF) is simply a function that operates on other functions, either by taking them as arguments or by returning them.
This guide explores how to leverage HOFs to write cleaner, more modular, and efficient code using built-in tools like map, filter, and the functools library.
Passing Functions as Arguments (map, filter)
The most common application of HOFs is data transformation. Instead of writing explicit loops to process lists, you pass a transformation function to a HOF.
Using map() for Transformation
map() applies a function to every item in an iterable.
def square(x):
return x ** 2
numbers = [1, 2, 3, 4, 5]
squared_loop = []
for num in numbers:
squared_loop.append(square(num))
squared_map = list(map(square, numbers))
print(f"Loop result: {squared_loop}")
print(f"Map result: {squared_map}")Output:
Loop result: [1, 4, 9, 16, 25]
Map result: [1, 4, 9, 16, 25]Using sorted() with a Key
The built-in sorted() function is a HOF because it accepts a key argument, i.e. a function that determines how to sort elements.
words = ["apple", "Banana", "cherry", "Date"]
print(f"Standard sort: {sorted(words)}")
print(f"HOF sort: {sorted(words, key=str.lower)}")Output:
Standard sort: ['Banana', 'Date', 'apple', 'cherry']
HOF sort: ['apple', 'Banana', 'cherry', 'Date']Returning Functions (Closures and Factories)
A HOF can create and return a new function. This is useful for creating "function factories" that configure a function with specific behaviors (Closures).
def make_multiplier(factor):
"""Higher-Order Function that returns a new function."""
def multiplier(x):
return x * factor
return multiplier
double = make_multiplier(2)
triple = make_multiplier(3)
print(f"Double 5: {double(5)}")
print(f"Triple 5: {triple(5)}")Output:
Double 5: 10
Triple 5: 15Partial Function Application
Sometimes you have an existing function with many arguments, and you want to create a new version with some arguments pre-filled. The functools.partial HOF handles this automatically.
from functools import partial
def power(base, exponent):
return base ** exponent
def square_manual(base):
return power(base, 2)
square_partial = partial(power, exponent=2)
cube_partial = partial(power, exponent=3)
print(f"Square of 4: {square_partial(4)}")
print(f"Cube of 4: {cube_partial(4)}")Output:
Square of 4: 16
Cube of 4: 64Decorators: The Ultimate HOF
Decorators are syntactic sugar for HOFs. A decorator is a function that takes a function as input and returns a modified function as output.
Creating a Simple Logger
import functools
def logger(func):
"""A HOF that adds logging to any function."""
@functools.wraps(func) # Preserves original function metadata
def wrapper(*args, **kwargs):
print(f"LOG: Running {func.__name__}")
result = func(*args, **kwargs)
print(f"LOG: Finished {func.__name__}")
return result
return wrapper
@logger
def add(x, y):
return x + y
print(f"Result: {add(5, 3)}")Output:
LOG: Running add
LOG: Finished add
Result: 8Conclusion
Higher-Order Functions allow you to abstract logic and behavior, making your code more reusable and declarative.
- Pass Functions: Use
map,filter, andsortedto apply logic to data collections. - Return Functions: Use closures to create configurable function factories.
- Partial Application: Use
functools.partialto fix arguments and simplify function signatures. - Decorators: Use
@decoratorsyntax to wrap functions with common behavior like logging or caching.