Detailed explanation of Higher-order Function

What is a higher-order function?

Let’s start with a small daily development scenario:

Suppose you want to write a file filtering tool:

  • Initially, only images with "size ≥ 10MB" are filtered, and a special function must be written;
  • Later, if I want to filter documents "created after 2023", I have to change the code;
  • Later I want to filter "File names containing_project"The code file... is too troublesome!

If you use higher-order functions to solve the problem, you only need to write a general screening framework, extract the judgment logic of "what is qualified" into a separate function, and pass it to the framework as a parameter.

Back to the definition: **Higher-order functions are a type of function that "can take other functions as parameters" or "can return other functions as results." ** It is the core concept of functional programming, which can help us change the code from "hard-coded logic" to "extracted reuse + flexible combination".


Prerequisite knowledge: "Functions are first-class citizens" in Python

The premise for playing with high-order functions in Python is: functions, like integers, strings, and lists, are "first-class citizens" and enjoy three core rights:

1. Variables can "point to" functions

# 取绝对值是内置函数 abs(),直接调用没问题
abs(-20)  # 输出:20

# 我们也可以把 abs 的“使用权”交给变量 f
f = abs
f(-20)    # 输出:20
# 这里的 f 和 abs 指向的是同一个函数对象

2. The essence of the function name is "the variable pointing to the function"

Be extra careful about this, never modify references to built-in functions casually:

# 作死操作:把 abs 变量指向整数 10
abs = 10
abs(-20)  # 直接报错:TypeError: 'int' object is not callable

# (别学!实际开发绝对不要这么做)恢复 abs 的方法
import builtins
abs = builtins.abs
abs(-20)  # 输出:20

⚠️ Important reminder: In production code, never overwrite references to built-in functions and modules!


The first major feature: functions as parameters

This is our most commonly used high-order function play - extract "specific operations" into functions and pass them to "general process".

1. A simple getting started example

def dual_operate(x: int, y: int, op: callable) -> int:
    """通用的「对两个数做同一种操作后合并」的流程"""
    return op(x) + op(y)

# 传入「取绝对值」操作
print(dual_operate(-4, 6, abs))  # 输出:10

# 传入「平方」操作(使用 lambda 更简洁)
print(dual_operate(-4, 6, lambda x: x**2))  # 输出:52

2. Three classic high-order parameter functions built into Python

map(): Batch "transform" data

It uses a "transformation function" to process each element of the iterable object (list, tuple, etc.) one by one, and returns an iterator (saving memory).

# 场景:把所有数字转成字符串
nums = [1, 3, 5, 7]
str_nums = list(map(str, nums))  # 转为 list 方便打印
print(str_nums)  # 输出:['1', '3', '5', '7']

# 场景:用 lambda 做自定义变换(将两个列表的对应元素相加)
a = [1, 2, 3]
b = [4, 5, 6]
sum_list = list(map(lambda x, y: x + y, a, b))
print(sum_list)  # 输出:[5, 7, 9]

filter(): Batch "filter" data

Use a "returnTrue/False"Judgment function (predicate)", leave the elements that meet the conditions, and also return the iterator.

# 场景:筛选偶数
nums = [1, 2, 3, 4, 5, 6, 7, 8]
even_nums = list(filter(lambda x: x % 2 == 0, nums))
print(even_nums)  # 输出:[2, 4, 6, 8]

functools.reduce():Batch "aggregate" data

Need to start withfunctoolsmodule import. It will stack the elements of the iterable object one by one and finally get a result.

from functools import reduce

# 场景:计算列表的乘积
nums = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, nums)
print(product)  # 输出:120

The second major feature: function as return value

High-order functions can not only "receive tools", but also "make tools" - returning different customized functions based on different inputs.

1. Closure: a tool to create “remember context”

A concept is introduced here - closure: If the returned internal function references the variables of the external function (even if the external function has been executed), then this internal function is called a closure.

def create_price_calculator(discount: float):
    """根据不同折扣,制造对应的价格计算器"""
    if not 0 <= discount <= 1:
        raise ValueError("折扣必须在 0 到 1 之间!")
    
    # 内部函数 calculate 会一直记住外部的 discount 变量
    def calculate(original_price: float) -> float:
        return original_price * (1 - discount)
    
    return calculate

# 制造两个不同折扣的计算器
no_discount = create_price_calculator(0.0)
half_price = create_price_calculator(0.5)

print(no_discount(100))  # 输出:100.0
print(half_price(100))  # 输出:50.0

2. Decorator: a tool for creating “adding special effects to functions”

This is the most commonly used and elegant combination of closure + higher-order function in Python! It can add common functions such as "logging, timing, and permission verification" to the function without modifying the original function code.

① The most basic decorator

import time

def timer(func: callable) -> callable:
    """给函数加「计时」特效的装饰器"""
    def wrapper(*args, **kwargs):
        # *args、**kwargs 可以接收原函数的任意参数
        start = time.time()
        result = func(*args, **kwargs)  # 执行原函数
        end = time.time()
        print(f"函数 {func.__name__} 执行耗时:{end - start:.4f} 秒")
        return result  # 别忘了返回原函数的结果
    return wrapper

# 用 @ 符号把装饰器「贴」在原函数上
@timer
def slow_add(a: int, b: int, delay: float = 1.0) -> int:
    time.sleep(delay)
    return a + b

print(slow_add(3, 5))  # 先睡 1 秒,打印耗时,再返回 8

Modern Python high-order function gadgets

1. functools.partial(): "Prefilled parameters" simplifies calling

If you have a function but often need to pass in certain fixed parameters, you can usepartial()Prefill them in to create a new, cleaner function.

from functools import partial
from operator import mul  # 内置的乘法函数,等价于 lambda x, y: x * y

# 场景:经常要算「某个数 × 2」,预填第一个参数 2
double = partial(mul, 2)
print(double(5))  # 输出:10

# 场景:预填第二个参数(使用关键字参数)
square = partial(mul, y=2)
print(square(7))  # 输出:14

2. Type annotation support

Python 3.5+typingModules can add standardized type annotations to high-order functions to improve readability and IDE completion experience.

from typing import Callable, TypeVar, List

# 定义通用类型变量 T、U,让函数支持任意输入输出类型
T = TypeVar('T')
U = TypeVar('U')

def my_map(func: Callable[[T], U], iterable: List[T]) -> List[U]:
    """手写一个简化版的 map(返回 list 版),带类型注解"""
    return [func(x) for x in iterable]

# 使用时 IDE 会自动提示类型错误
# 例如将 int 列表转成 str 列表,结果类型是 List[str]
int_list = [1, 2, 3]
str_list = my_map(str, int_list)

Best practices for higher-order functions

  • Parameter naming should be clear: If the parameters of higher-order functions are functions, try to useop(operate),predicate(predicate/judgment),key_func(key function used for sorting) this kind of meaningful name, rather thanfg
  • Internal logic should be simple: High-order functions are only responsible for "general processes". Do not stuff specific business logic into them, otherwise you will lose flexibility.
  • Don’t modify external variables in closures: If you really want to modify them, you must usenonlocalkeyword (Python 3+), otherwise it will be treated as a local variable of the inner function.
  • Be cautious in performance-sensitive scenarios: Function calls have a certain overhead, and excessive use of higher-order functions in a large number of loops may slow things down (such as using list comprehensions instead of simplemap / filterwill be faster).

Summarize

High-order functions achieve abstraction, reuse, and flexible combination of code by "treating functions as first-class citizens":

  • Function as parameter: You can separate "general process" and "specific operation";
  • Function as return value: You can create customized tools that "remember context" or "add special effects";
  • Python built-in tools:mapfilterfunctools.reducefunctools.partial, decorator.

Mastering higher-order functions is an important step from "Getting Started with Python" to "Advanced Python" and can help you write simpler, more elegant, and easier-to-maintain code!