Python functools模块使用教程

Python functools 模块使用教程

functools 是 Python 标准库中用于高阶函数和函数式编程的工具模块,提供了许多有用的函数装饰器和工具。

目录

  1. 函数装饰器
  2. 函数操作工具
  3. 函数比较与排序
  4. 高阶函数工具
  5. 高级应用与技巧
  6. 性能考虑

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 模块提供了许多强大的工具:

  1. 通过装饰器增强函数功能(缓存、排序、属性管理等)
  2. 创建部分应用的函数和方法
  3. 实现函数式编程模式(reduce、柯里化等)
  4. 构建灵活的API(单分派泛型函数)
  5. 保留函数元数据(wraps)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值