Python functools
模块使用教程
functools
是 Python 标准库中用于高阶函数和函数式编程的工具模块,提供了许多有用的函数装饰器和工具。
目录
1. 函数装饰器
@lru_cache
最近最少使用缓存装饰器,用于函数结果的缓存。
from functools import lru_cache
import time
@lru_cache(maxsize=32) # 最多缓存32个结果
def slow_function(x):
time.sleep(2) # 模拟耗时操作
return x * x
# 第一次调用会耗时
print(slow_function(4)) # 大约2秒后输出: 16
# 相同参数的第二次调用会立即返回缓存结果
print(slow_function(4)) # 立即输出: 16
# 不同参数会重新计算
print(slow_function(5)) # 大约2秒后输出: 25
# 查看缓存信息
print(slow_function.cache_info()) # 输出: CacheInfo(hits=1, misses=2, maxsize=32, currsize=2)
# 清空缓存
slow_function.cache_clear()
参数说明:
maxsize
: 缓存的最大数量,设为None表示无限制(不推荐),设为0表示禁用缓存typed
: 如果为True,不同类型的参数会被分别缓存,如3和3.0
@total_ordering
自动填充缺失的比较方法的类装饰器。
from functools import total_ordering
@total_ordering
class Student:
def __init__(self, name, grade):
self.name = name
self.grade = grade
def __eq__(self, other):
return self.grade == other.grade
def __lt__(self, other):
return self.grade < other.grade
# 虽然只定义了__eq__和__lt__,但装饰器会自动补全其他比较方法
s1 = Student('Alice', 85)
s2 = Student('Bob', 90)
print(s1 <= s2) # True
print(s1 > s2) # False
print(s1 != s2) # True
@cache
Python 3.9+ 新增的简单缓存装饰器,相当于 @lru_cache(maxsize=None)
。
from functools import cache
@cache
def factorial(n):
return n * factorial(n-1) if n else 1
print(factorial(10)) # 3628800
print(factorial.cache_info()) # 查看缓存信息
@cached_property
Python 3.8+ 新增的将方法转换为惰性求值属性的装饰器。
from functools import cached_property
class DataSet:
def __init__(self, sequence):
self._data = sequence
@cached_property
def stdev(self):
print("Computing stdev...")
# 这里放复杂的计算逻辑
return sum(self._data) / len(self._data)
ds = DataSet([1, 2, 3, 4, 5])
print(ds.stdev) # 第一次访问会计算并缓存
print(ds.stdev) # 第二次访问直接返回缓存值
# 注意:如果实例属性被修改,缓存不会自动更新
ds._data = [10, 20, 30]
print(ds.stdev) # 仍然返回旧值
2. 函数操作工具
partial
部分应用函数,固定某些参数。
from functools import partial
def power(base, exponent):
return base ** exponent
# 固定base为2
square = partial(power, exponent=2)
cube = partial(power, exponent=3)
print(square(5)) # 25 (5^2)
print(cube(3)) # 27 (3^3)
# 也可以固定多个参数
pow2 = partial(power, 2) # 固定base为2
print(pow2(10)) # 1024 (2^10)
# 可以更新部分应用的参数
new_pow = pow2.func # 获取原始函数
print(new_pow(3, 4)) # 81 (3^4)
partialmethod
类似于 partial
,但用于类方法。
from functools import partialmethod
class Cell:
def __init__(self):
self._alive = False
@property
def alive(self):
return self._alive
def set_state(self, state):
self._alive = bool(state)
# 创建部分方法
set_alive = partialmethod(set_state, True)
set_dead = partialmethod(set_state, False)
cell = Cell()
print(cell.alive) # False
cell.set_alive()
print(cell.alive) # True
cell.set_dead()
print(cell.alive) # False
reduce
将二元操作函数累积应用到序列元素上。
from functools import reduce
# 计算阶乘
def factorial(n):
return reduce(lambda x, y: x * y, range(1, n+1))
print(factorial(5)) # 120
# 连接字符串列表
words = ['Python', 'is', 'awesome']
sentence = reduce(lambda x, y: f"{x} {y}", words)
print(sentence) # "Python is awesome"
# 带初始值
print(reduce(lambda x, y: x + y, [1, 2, 3, 4], 10)) # 20 (10+1+2+3+4)
wraps
用于保留被装饰函数的元数据。
from functools import wraps
def my_decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
"""Wrapper docstring"""
print(f"Calling {f.__name__}")
return f(*args, **kwargs)
return wrapper
@my_decorator
def example():
"""Example docstring"""
print("Inside example function")
example()
print(example.__name__) # 输出 "example" 而不是 "wrapper"
print(example.__doc__) # 输出 "Example docstring" 而不是 "Wrapper docstring"
3. 函数比较与排序
cmp_to_key
将老式的比较函数转换为key函数。
from functools import cmp_to_key
def compare(x, y):
"""按长度排序,长度相同则按字母顺序"""
if len(x) == len(y):
return -1 if x < y else 1
return len(x) - len(y)
words = ['apple', 'banana', 'cherry', 'date', 'fig']
sorted_words = sorted(words, key=cmp_to_key(compare))
print(sorted_words) # ['fig', 'date', 'apple', 'cherry', 'banana']
4. 高阶函数工具
singledispatch
单分派泛型函数装饰器,根据第一个参数类型选择实现。
from functools import singledispatch
@singledispatch
def process(data):
"""默认处理"""
print(f"处理未知类型: {type(data).__name__}")
@process.register(str)
def _(text):
print(f"处理字符串: {text}")
@process.register(int)
def _(number):
print(f"处理整数: {number}")
@process.register(list)
def _(lst):
print(f"处理列表,长度: {len(lst)}")
process(42) # 处理整数: 42
process("hello") # 处理字符串: hello
process([1, 2, 3]) # 处理列表,长度: 3
process(3.14) # 处理未知类型: float
singledispatchmethod
Python 3.8+ 新增,类似于 singledispatch
但用于类方法。
from functools import singledispatchmethod
class Formatter:
@singledispatchmethod
def format(self, arg):
return f"默认格式: {arg}"
@format.register(int)
def _(self, arg):
return f"整数格式: {arg:,}"
@format.register(float)
def _(self, arg):
return f"浮点数格式: {arg:.2f}"
fmt = Formatter()
print(fmt.format(1000000)) # 整数格式: 1,000,000
print(fmt.format(3.1415926)) # 浮点数格式: 3.14
print(fmt.format("text")) # 默认格式: text
5. 高级应用与技巧
1. 动态装饰器
from functools import wraps
def debug(prefix=''):
"""带参数的装饰器工厂"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"{prefix}调用 {func.__name__},参数: {args}, {kwargs}")
result = func(*args, **kwargs)
print(f"{prefix}返回: {result}")
return result
return wrapper
return decorator
@debug(prefix='[TEST] ')
def add(x, y):
return x + y
add(3, 5)
# 输出:
# [TEST] 调用 add,参数: (3, 5), {}
# [TEST] 返回: 8
2. 组合多个装饰器
from functools import lru_cache, wraps
def log_calls(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"调用 {func.__name__} 参数: {args}, {kwargs}")
return func(*args, **kwargs)
return wrapper
@log_calls
@lru_cache(maxsize=100)
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(10))
3. 函数柯里化
from functools import partial
def curry(f):
"""柯里化装饰器"""
@wraps(f)
def curried(*args, **kwargs):
if len(args) + len(kwargs) >= f.__code__.co_argcount:
return f(*args, **kwargs)
return partial(curried, *args, **kwargs)
return curried
@curry
def add_three(a, b, c):
return a + b + c
print(add_three(1)(2)(3)) # 6
print(add_three(1, 2)(3)) # 6
print(add_three(1)(2, 3)) # 6
print(add_three(1, 2, 3)) # 6
4. 带状态的函数
from functools import partial
def with_state(func):
"""添加状态属性的装饰器"""
func.state = 0
@wraps(func)
def wrapper(*args, **kwargs):
wrapper.state += 1
return func(*args, **kwargs)
wrapper.state = 0
return wrapper
@with_state
def counter():
pass
counter()
counter()
print(counter.state) # 2
6. 性能考虑
缓存装饰器:
@lru_cache
适合计算密集型函数,参数较少且可哈希- 缓存会消耗内存,应根据需要设置合理的
maxsize
- 对于纯函数(无副作用)最适合使用缓存
partial
vs lambda:
partial
通常比 lambda 更高效且更清晰partial
对象保留了原始函数的引用,而 lambda 会创建新的作用域
reduce
:
- 在 Python 中,显式循环通常比
reduce
更易读 - 但在某些函数式编程场景下,
reduce
仍然很有用
总结
functools
模块提供了许多强大的工具:
- 通过装饰器增强函数功能(缓存、排序、属性管理等)
- 创建部分应用的函数和方法
- 实现函数式编程模式(reduce、柯里化等)
- 构建灵活的API(单分派泛型函数)
- 保留函数元数据(wraps)