← 返回Python标准库精讲目录
← 返回学习笔记首页
专题: Python标准库精讲系统学习
关键词: Python, 标准库, functools, reduce, partial, lru_cache, cache, wraps, singledispatch, total_ordering
一、functools 模块概述
functools 是 Python 标准库中专门为高阶函数(higher-order functions)提供的工具模块。所谓高阶函数,是指那些以函数作为参数或返回值的函数。functools 模块汇集了一系列用于操作和增强函数功能的实用工具,是函数式编程风格的核心支撑模块。
该模块的主要工具可分为五大类:归约操作(reduce)、偏函数(partial)、缓存装饰器(lru_cache / cache / cached_property)、函数元信息维护(wraps / update_wrapper)、以及泛函数机制(singledispatch / singledispatchmethod)。此外还包含排序辅助工具(cmp_to_key)和比较方法自动生成器(total_ordering)。
functools 在 Python 3 中得到持续增强。Python 3.9 引入了 cache 装饰器作为 lru_cache 的无上限简化版本;Python 3.8 引入了 singledispatchmethod 用于方法级别的单分派;Python 3.10 为 singledispatch 增加了 union 类型支持。理解 functools 是掌握 Python 函数式编程进阶技巧的关键一步。
核心要点: functools 提供的不是"新功能",而是"函数操作的语法糖"——它让你用更少的代码表达更强的函数逻辑,是减少重复、提升抽象层次的标准工具集。
二、reduce 归约
2.1 reduce 函数的基本用法
reduce 函数源自函数式编程中经典的"fold"(折叠)操作,它将一个二元操作函数累积地应用到序列的元素上,从而将序列归约为单个值。其函数签名为 reduce(function, iterable[, initial])。
工作流程:首先从可迭代对象中取出前两个元素(若有初始值则取初始值与第一个元素),应用二元函数得到中间结果;然后将中间结果与下一个元素继续应用该函数,直到遍历完所有元素,返回最终值。
from functools import reduce
# 计算 1+2+3+4+5 = 15
result = reduce(lambda x, y: x + y, [1, 2, 3, 4, 5])
print(result) # 15
# 计算 5! = 120
factorial = reduce(lambda x, y: x * y, range(1, 6))
print(factorial) # 120
# 列表扁平化:[[1,2], [3,4], [5]] -> [1,2,3,4,5]
flattened = reduce(lambda acc, item: acc + item, [[1, 2], [3, 4], [5]], [])
print(flattened) # [1, 2, 3, 4, 5]
2.2 初始值(initial)的意义
initial 参数提供归约的起始值。当提供了 initial,reduce 从 initial 和序列的第一个元素开始操作。初始值的核心价值有两方面:其一,当序列为空时,直接返回 initial 而不会抛出 TypeError;其二,对非交换、非结合的操作而言,初始值决定了计算顺序的基准。
例如计算乘积时提供初始值 1,计算列表拼接时提供空列表 [] 作为初始值,都可以保证即使输入为空也能返回有意义的结果。
# 提供一个初始值,空序列也不报错
safe_sum = reduce(lambda x, y: x + y, [], 0)
print(safe_sum) # 0
# 不提供初始值时空序列抛出 TypeError
try:
reduce(lambda x, y: x + y, [])
except TypeError as e:
print(f"无初始值+空序列: {e}")
# 用初始值 1 计算连乘
product = reduce(lambda x, y: x * y, [2, 3, 4], 1)
print(product) # 24
2.3 右折叠(reduce 的左倾性)
Python 的 reduce 本质上是"左折叠"(left fold),即从左向右累积。在某些场景下需要"右折叠"(right fold),即从右向左操作。实现右折叠的最简单方式是对序列取反后再应用 reduce,或者使用自定义的右折叠函数。
左折叠与右折叠的区别在减法、除法等非结合操作中尤为明显。例如 reduce(sub, [1,2,3]) 等价于 (1-2)-3 = -4,而右折叠应当等价于 1-(2-3) = 2。
# 左折叠: (((1 - 2) - 3) - 4) = -8
left_fold = reduce(lambda x, y: x - y, [1, 2, 3, 4])
print(f"左折叠: {left_fold}") # -8
# 右折叠: (1 - (2 - (3 - 4))) = -2
right_fold = reduce(lambda x, y: y - x, reversed([1, 2, 3, 4]))
print(f"右折叠: {right_fold}") # -2
2.4 reduce 与 sum / any / all 的对比
Python 为常见的归约操作提供了专用函数,这些函数在可读性和性能上通常优于 reduce。推荐的使用原则是:有专用函数的优先使用专用函数,没有专用函数的再考虑 reduce。
操作 专用函数 reduce 等价写法 推荐
求和 sum(iterable, start=0) reduce(add, iterable, 0) sum 更优(C 实现,更快)
逻辑或 any(iterable) reduce(or_, iterable, False) any 更优(短路求值)
逻辑与 all(iterable) reduce(and_, iterable, True) all 更优(短路求值)
最大值 max(iterable) reduce(max, iterable) max 更优
最小值 min(iterable) reduce(min, iterable) min 更优
连乘 math.prod (3.8+) reduce(mul, iterable, 1) math.prod 更优
reduce 的独特价值在于那些没有专用函数的归约操作,例如求最大公约数、字符串交替拼接、嵌套字典合并等自定义累积逻辑。
三、partial 偏函数
3.1 偏函数的概念与使用
partial 用于"冻结"一个函数的某些参数(位置参数或关键字参数),从而创建一个参数更少的新函数。这在函数式编程中称为"偏应用"(partial application),是柯里化(currying)的一种简化形式。
partial 函数签名为 functools.partial(func, /, *args, **keywords),返回一个可调用的 partial 对象。当调用这个 partial 对象时,它会将预设的参数和调用时传入的参数合并后转发给原始函数。
from functools import partial
def power(base, exponent):
return base ** exponent
# 创建固定指数的偏函数
square = partial(power, exponent=2)
cube = partial(power, exponent=3)
print(square(5)) # 25
print(cube(5)) # 125
print(power(2, 10)) # 1024(原始函数不受影响)
# 固定 base 也可以
two_power = partial(power, 2) # 固定 base=2
print(two_power(10)) # 1024
3.2 partial 对象的内部属性
partial 返回的对象具有三个只读属性,分别揭示了偏函数绑定的参数信息:
func :原始函数对象
args :预设的位置参数元组(在调用时放在所有用户传入的位置参数之前)
keywords :预设的关键字参数字典
from functools import partial
def log(level, message, timestamp=None):
parts = [f"[{level}]", message]
if timestamp:
parts.append(f"({timestamp})")
return " ".join(parts)
info_log = partial(log, "INFO", timestamp="2026-05-05")
print(info_log.func) #
print(info_log.args) # ('INFO',)
print(info_log.keywords) # {'timestamp': '2026-05-05'}
# 调用时还可以继续传参
print(info_log("系统启动")) # [INFO] 系统启动 (2026-05-05)
# 调用时传入的 message 会放在 args 的已有值之后
# 相当于 log('INFO', '系统启动', timestamp='2026-05-05')
3.3 偏函数的实际应用场景
偏函数在以下几个场景中特别有用:
回调适配 :当一个回调函数只接受部分参数,而事件源会传递更多参数时,用 partial 将多余参数固定
API 封装 :将需要反复传入默认参数的函数调用封装为更简洁的接口
减少 lambda 使用 :许多简单的 lambda 表达式可以用 partial 替代,代码更清晰
多进程/多线程参数绑定 :Pool.map 只能传一个参数,partial 可以冻结其他参数
from functools import partial
# 场景一:替代简单的 lambda
# 不推荐: lambda x: pow(x, 2)
# 推荐:
square = partial(pow, 2) # pow(2, x) 而不是 pow(x, 2)!
# 注意顺序:partial(pow, 2) 相当于固定第一个参数为 2
# 所以 pow(2, x) 其实是 2**x,这不是平方而是 2 的幂
# 正确写法:partial(pow, exp=2) 或自定义函数
# 场景二:多进程参数绑定
from multiprocessing import Pool
def process_file(file_path, encoding="utf-8"):
# 处理文件的逻辑
return f"处理 {file_path} (编码: {encoding})"
files = ["a.txt", "b.txt", "c.txt"]
process_with_encoding = partial(process_file, encoding="gbk")
# with Pool(4) as pool:
# results = pool.map(process_with_encoding, files)
# 场景三:GUI 回调绑定
# button.on_click(partial(handler, user_id=42))
四、缓存装饰器
4.1 lru_cache 最近最少使用缓存
lru_cache 是 functools 中最常用的缓存装饰器,它通过"最近最少使用"(Least Recently Used)淘汰策略自动缓存函数的返回值。对于计算密集型或 I/O 密集型的纯函数(相同输入总是返回相同输出),lru_cache 可以显著提升性能。
其函数签名为 functools.lru_cache(maxsize=128, typed=False)。maxsize 指定缓存的最大条目数(设为 None 表示无限制),typed 为 True 时区分不同参数类型(如 1 和 1.0 视为不同调用)。
from functools import lru_cache
import time
# 未缓存版本
def fib_slow(n):
if n < 2:
return n
return fib_slow(n-1) + fib_slow(n-2)
# 缓存版本
@lru_cache(maxsize=128)
def fib_fast(n):
if n < 2:
return n
return fib_fast(n-1) + fib_fast(n-2)
# 性能对比
start = time.time()
fib_slow(35)
print(f"未缓存: {time.time() - start:.3f}s") # 约 2~4 秒
start = time.time()
fib_fast(35)
print(f"已缓存: {time.time() - start:.3f}s") # 毫秒级
print(fib_fast.cache_info())
# CacheInfo(hits=32, misses=36, maxsize=128, currsize=36)
lru_cache 对象提供了几个有用的方法:cache_info() 返回命中次数、未命中次数、最大容量和当前大小;cache_clear() 清空缓存;cache_parameters() 返回配置参数。
4.2 cache 无限缓存(Python 3.9+)
cache 是 Python 3.9 引入的简化版缓存装饰器,等价于 lru_cache(maxsize=None),即不限制缓存大小。当你确定缓存数量可控或希望尽可能多地缓存结果时,cache 比 lru_cache 更简洁。
使用 cache 时所有返回值都会被永久缓存(直到进程重启或手动清除),因此不适合参数组合无限多的函数(如接受用户输入的函数),否则会导致内存泄漏。
from functools import cache
@cache
def expensive_computation(n):
"""模拟一个耗时计算"""
print(f"正在计算 {n}...")
return n * n
print(expensive_computation(10)) # 计算并缓存
print(expensive_computation(10)) # 直接返回缓存,不会输出"正在计算"
print(expensive_computation(20)) # 计算并缓存
# cache 等价于
# @lru_cache(maxsize=None)
# def expensive_computation(n): ...
4.3 cached_property 属性缓存
cached_property 是 Python 3.8 引入的装饰器,它将一个类方法转换为惰性求值的缓存属性。与 property 不同,cached_property 只在首次访问时调用被装饰的方法,之后将结果缓存起来,后续访问直接返回缓存值而不重新计算。
与 property 的关键区别:cached_property 的结果会被存储在实例的 __dict__ 中(键名为属性名),这意味着它只能用于没有同名实例属性的场景,且缓存值可以被直接删除以触发重新计算。
from functools import cached_property
import time
class DataProcessor:
def __init__(self, data):
self.data = data
@cached_property
def processed(self):
"""昂贵的计算,只执行一次"""
print("执行复杂数据处理...")
time.sleep(1) # 模拟耗时
return [x * 2 for x in self.data]
@property
def summary(self):
"""普通 property,每次调用都重新计算"""
print("重新生成摘要...")
return f"数据共 {len(self.data)} 条"
dp = DataProcessor([1, 2, 3, 4, 5])
# 第一次访问:触发计算
print(dp.processed) # 输出 "[2, 4, 6, 8, 10]" 并打印"执行"
# 第二次访问:立即返回缓存
print(dp.processed) # 只输出 "[2, 4, 6, 8, 10]"
# 删除缓存可触发重新计算
del dp.processed
print(dp.processed) # 再次打印"执行"并计算
使用建议: lru_cache 适合有明确上限的场景(如递归、固定参数集);cache 适合参数组合有限且确定不会无限增长的场景;cached_property 适合类中那些计算一次后不再变化的派生属性。对于可变对象或依赖外部状态的函数,切勿使用任何缓存装饰器。
五、wraps 与更新
5.1 装饰器对函数元信息的破坏
Python 装饰器的本质是将原函数替换为包装函数(wrapper),这会导致原函数的元信息丢失——包括 __name__、__doc__、__module__、__annotations__ 以及 __dict__ 等属性全部被 wrapper 函数覆盖。这对调试、文档生成、序列化等场景造成困扰。
def my_decorator(func):
def wrapper(*args, **kwargs):
"""包装函数的文档"""
print("调用前")
result = func(*args, **kwargs)
print("调用后")
return result
return wrapper
@my_decorator
def greet(name):
"""向指定的人打招呼"""
print(f"你好, {name}!")
print(greet.__name__) # wrapper(丢失了原名)
print(greet.__doc__) # 包装函数的文档(丢失了原文档)
5.2 wraps 保留原函数元信息
wraps 是 functools 提供的一个装饰器,用于在定义 wrapper 函数时自动将原函数的元信息复制过来。它是 update_wrapper 的便捷装饰器版本,本质上是 partial(update_wrapper, wrapped=func, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)。
from functools import wraps
def my_decorator(func):
@wraps(func) # 关键:将 func 的元信息复制到 wrapper
def wrapper(*args, **kwargs):
"""包装函数的文档"""
print("调用前")
result = func(*args, **kwargs)
print("调用后")
return result
return wrapper
@my_decorator
def greet(name):
"""向指定的人打招呼"""
print(f"你好, {name}!")
print(greet.__name__) # greet(原函数名保留)
print(greet.__doc__) # 向指定的人打招呼(原文档保留)
5.3 update_wrapper 与 WRAPPER_ASSIGNMENTS
update_wrapper 是 wraps 的底层实现,它直接将 wrapped 函数的属性复制到 wrapper 函数。你可以通过修改 WRAPPER_ASSIGNMENTS 和 WRAPPER_UPDATES 来控制复制哪些属性。
默认情况下,WRAPPER_ASSIGNMENTS 包含 __module__、__name__、__qualname__、__annotations__、__doc__ 五个属性;WRAPPER_UPDATES 包含 __dict__,表示更新 wrapper 的 __dict__(合并而非覆盖)。
from functools import update_wrapper, WRAPPER_ASSIGNMENTS, WRAPPER_UPDATES
print("默认复制属性:", WRAPPER_ASSIGNMENTS)
# ('__module__', '__name__', '__qualname__', '__annotations__', '__doc__')
print("默认更新属性:", WRAPPER_UPDATES)
# ('__dict__',)
# 手动使用 update_wrapper
def decorator(func):
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return update_wrapper(wrapper, func)
# 自定义:只复制 name 和 doc
def minimal_wrapper(wrapper, wrapped):
wrapper.__name__ = wrapped.__name__
wrapper.__doc__ = wrapped.__doc__
return wrapper
最佳实践: 在编写自定义装饰器时始终使用 @wraps(func)。这不仅有助于调试(堆栈信息显示正确的函数名),还能保证文档字符串自动继承,是合格装饰器的基本功。
六、单分派泛函数
6.1 singledispatch 的概念与意义
singledispatch 为 Python 提供了"单分派泛函数"(Single-Dispatch Generic Function)机制。它允许你根据第一个参数的类型来动态选择不同的函数实现,类似于某些语言中的方法重载(overloading),但 Python 的动态类型系统需要运行时派发。
所谓"单分派"(single dispatch)是指只根据一个参数(通常是第一个参数)的类型进行分派,与之相对的是"多分派"(multiple dispatch),后者根据多个参数的类型进行分派。
from functools import singledispatch
@singledispatch
def process(value):
"""处理各种类型的数据"""
raise TypeError(f"不支持的类型: {type(value)}")
@process.register(int)
def _(value):
"""处理整数"""
return f"整数: {value} ({value:#x})"
@process.register(str)
def _(value):
"""处理字符串"""
return f"字符串: {value!r} (长度 {len(value)})"
@process.register(list)
@process.register(tuple)
def _(value):
"""处理序列"""
return f"序列: 共 {len(value)} 个元素, 首项={value[0] if value else None}"
print(process(42)) # 整数: 42 (0x2a)
print(process("hello")) # 字符串: 'hello' (长度 5)
print(process([1, 2])) # 序列: 共 2 个元素, 首项=1
6.2 register 注册重载
register 是 singledispatch 对象的核心方法,用于为特定类型注册专门的函数实现。它可以作为装饰器使用(如上例),也可以直接调用 register(type, func) 来注册已经定义的函数。
注册函数时,函数名可以任意(通常使用 _ 表示这些函数不打算被外部直接调用)。多个类型可以注册到同一个实现(如上例中的 list 和 tuple 共用一个实现)。
from functools import singledispatch
@singledispatch
def format_output(value):
return f"未知类型: {value}"
# 方式一:装饰器注册
@format_output.register(float)
def _(value):
return f"浮点数: {value:.2f}"
# 方式二:直接调用 register 注册已定义的函数
def format_bool(value):
return f"布尔值: {'真' if value else '假'}"
format_output.register(bool, format_bool)
# 方式三:为 None 注册
format_output.register(type(None), lambda _: "空值 None")
print(format_output(3.14159)) # 浮点数: 3.14
print(format_output(True)) # 布尔值: 真
print(format_output(None)) # 空值 None
6.3 dispatch 查找与类型层次
singledispatch 对象还提供了 dispatch(cls) 方法,用于查看给定类型会分派到哪个具体的函数实现。这在调试和测试中非常有用。
类型分派的查找遵循 MRO(方法解析顺序,Method Resolution Order)规则。如果某个类型没有直接注册的处理函数,singledispatch 会沿着 MRO 链向上查找最接近的父类型注册的实现。如果最终也没有找到匹配的,则调用 base 函数(默认行为)。
from functools import singledispatch
@singledispatch
def handler(value):
return "默认处理"
@handler.register(int)
def _(value):
return "整数处理"
# dispatch 查看分派目标
print(handler.dispatch(int)) #
print(handler.dispatch(str)) # (默认)
# 类型层次继承
class Animal: pass
class Dog(Animal): pass
class Cat(Animal): pass
@singledispatch
def speak(animal):
return "未知动物"
@speak.register(Animal)
def _(animal):
return "某种动物"
@speak.register(Dog)
def _(animal):
return "汪汪!"
# Cat 没有注册,沿着 MRO 找到 Animal
print(speak(Dog())) # 汪汪!
print(speak(Cat())) # 某种动物(沿 MRO 找到 Animal)
# 查看所有注册类型
print(speak.registry)
# {: , : , : }
6.4 singledispatchmethod 方法级单分派
singledispatchmethod 是 Python 3.8 引入的装饰器,将 singledispatch 的能力扩展到类方法上。与 singledispatch 不同,它可以正确处理绑定方法调用,即 self 参数不会被纳入分派,分派的是第一个非 self 参数。
from functools import singledispatchmethod
class PrettyPrinter:
@singledispatchmethod
def display(self, value):
print(f"默认: {value}")
@display.register(int)
def _(self, value):
print(f"整数: {value:,}")
@display.register(str)
def _(self, value):
print(f"字符串: 「{value}」")
@display.register(list)
def _(self, value):
print(f"列表 [{len(value)}项]: {value[:3]}{'...' if len(value) > 3 else ''}")
pp = PrettyPrinter()
pp.display(1234567) # 整数: 1,234,567
pp.display("你好世界") # 字符串: 「你好世界」
pp.display([1, 2, 3, 4]) # 列表 [4项]: [1, 2, 3, 4]...
注意: singledispatchmethod 的 register 装饰器要求注册的函数接受 self 作为第一个参数,这与普通的 singledispatch 不同。从 Python 3.10 开始,register 支持使用类型注解语法(format_output.register 与 format_output.register(int) 两种形式均可)。
七、比较与排序
7.1 total_ordering 自动生成比较方法
total_ordering 是一个类装饰器,它根据你定义的 __eq__ 和另外一个比较方法(__lt__、__le__、__gt__、__ge__ 中的任意一个),自动生成其余的三个比较方法。这避免了手动编写所有六个比较方法(__eq__、__ne__、__lt__、__le__、__gt__、__ge__)的重复劳动。
其实现原理很简单:利用代数关系进行推导。例如,有了 __eq__ 和 __lt__,就可以推导出 __le__(a <= b 等价于 a < b or a == b)、__gt__(a > b 等价于 b < a)、__ge__(a >= b 等价于 not a < b)和 __ne__(a != b 等价于 not a == b)。
from functools import total_ordering
@total_ordering
class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
def __eq__(self, other):
if not isinstance(other, Student):
return NotImplemented
return self.grade == other.grade
def __lt__(self, other):
if not isinstance(other, Student):
return NotImplemented
return self.grade < other.grade
def __repr__(self):
return f"{self.name}({self.grade})"
# 现在自动拥有了 __le__, __gt__, __ge__, __ne__
s1 = Student("Alice", 85)
s2 = Student("Bob", 92)
s3 = Student("Charlie", 85)
print(s1 < s2) # True(自定义)
print(s1 <= s2) # True(自动生成)
print(s2 > s1) # True(自动生成)
print(s2 >= s3) # True(自动生成)
print(s1 != s2) # True(自动生成)
print(s1 == s3) # True(自定义)
total_ordering 的性能开销:自动生成的方法是通过元编程实现的,在频繁比较的场景下(如排序数百万个对象),手动实现所有六个方法的性能会更好。但对于大多数日常场景,性能差异可以忽略不计。
7.2 cmp_to_key 自定义排序
cmp_to_key 是一个将旧式比较函数(cmp function)转换为键函数(key function)的适配器。在 Python 3 中,sorted()、list.sort()、max()、min() 等函数不再接受 cmp 参数,只接受 key 参数。cmp_to_key 帮助那些已经写好了比较函数的代码平滑迁移到 Python 3。
比较函数的签名是 cmp(a, b) -> int,返回负数表示 a < b,正数表示 a > b,零表示 a == b。cmp_to_key 将这种比较函数封装为一个实现了 __lt__ 等比较方法的可比较对象。
from functools import cmp_to_key
# 定义一个比较函数:按字符串长度降序,长度相同时按字母序升序
def custom_cmp(a, b):
if len(a) != len(b):
return len(b) - len(a) # 长度降序
if a < b:
return -1 # 字母序升序
if a > b:
return 1
return 0
words = ["apple", "pear", "banana", "kiwi", "grape", "fig"]
sorted_words = sorted(words, key=cmp_to_key(custom_cmp))
print(sorted_words)
# ['banana', 'apple', 'grape', 'pear', 'kiwi', 'fig']
# banana(6)>apple(5)/grape(5)/pear(4)/kiwi(4)/fig(3)
最佳实践: 在 Python 3 中,优先使用 key 参数结合 lambda 或 operator 模块(如 operator.attrgetter)来实现自定义排序。cmp_to_key 主要用于兼容遗留代码,新代码应直接使用 key 表达式。
八、核心总结
functools 模块工具一览
reduce —— 归约折叠,将序列累积为单个值。适用于没有专用函数的自定义累积操作,但应优先使用 sum/any/all/max/min 等专用函数。
partial —— 偏函数,冻结函数的某些参数生成新函数。适用于回调适配、API 封装、参数绑定等场景,是替代简单 lambda 的更优雅方案。
lru_cache / cache —— 缓存装饰器,自动缓存函数返回值。lru_cache 有容量上限(LRU 淘汰),cache 无上限(3.9+),两者都要求被装饰函数是纯函数(无副作用、不依赖外部状态)。
cached_property —— 属性缓存,将类方法变为只计算一次的缓存属性。适用于计算成本高但结果不变的派生属性。
wraps / update_wrapper —— 函数元信息维护。在自定义装饰器中始终使用 @wraps(func) 来保留原函数的 __name__、__doc__ 等重要属性。
singledispatch / singledispatchmethod —— 单分派泛函数,根据参数类型动态分派到不同的函数实现。适用于类型相关的多分支处理逻辑,比长长的 if-elif-else 更可维护。
total_ordering —— 比较方法自动生成。定义了 __eq__ 和 __lt__(或其他一个)后自动补全所有比较方法,减少样板代码。
cmp_to_key —— 比较函数适配器。将 Python 2 风格的 cmp 函数转换为 key 函数,主要用于向下兼容。
functools 模块的哲学可以概括为一句话:让函数操作函数 。它提供的每个工具都在尝试减少重复代码、提升抽象层次、让代码更具声明性。掌握 functools,意味着你不再仅仅编写"函数",而是开始编写"操作函数的函数"——这是函数式编程思维成熟的重要标志。
实际项目中,建议从最常用的几项开始:用 @wraps 规范装饰器编写、用 @lru_cache 优化纯函数性能、用 partial 简化重复参数传递。逐步深入后再掌握 singledispatch 和 total_ordering 等更高级的特性。