杰瑞科技汇

Python functools模块核心功能是什么?

functools 是 Python 标准库中的一个模块,它提供了用于高阶函数(即操作其他函数的函数)的工具,这个模块里的函数可以帮助你更灵活、更强大地使用函数。

Python functools模块核心功能是什么?-图1
(图片来源网络,侵删)

functools 的核心思想是函数式编程,它鼓励将函数视为一等公民(可以作为参数传递、作为返回值、存储在变量中)。

下面,我将分点介绍 functools 中最常用和最重要的几个函数。


functools.partial - 偏函数

partial 的作用是“冻结”一个函数的部分参数,从而创建一个新的、参数更少的函数,这在需要固定某些参数的场景下非常有用。

想象一个场景: 你有一个函数,它需要多个参数,但你经常在调用它时只改变其中一个参数,而其他参数保持不变。

Python functools模块核心功能是什么?-图2
(图片来源网络,侵删)

语法: functools.partial(func, *args, **keywords)

  • func: 要绑定的函数。
  • *args: 要绑定的位置参数(从左到右绑定)。
  • **keywords: 要绑定的关键字参数。

示例 1:固定一个参数

import functools
# 原始函数,需要两个参数
def power(base, exponent):
    return base ** exponent
# 创建一个新的函数,固定 exponent 为 2
square = functools.partial(power, exponent=2)
# 现在调用 square 只需要一个参数
print(square(4))   # 输出: 16
print(square(5))   # 输出: 25
# 也可以固定位置参数
cube = functools.partial(power, 3) # 固定 base 为 3
print(cube(4))     # 输出: 81 (3**4)
print(cube(2))     # 输出: 9  (3**2)

示例 2:在回调函数中常用

假设你有一个 GUI 库,按钮点击事件会调用一个函数,并自动传入一个事件对象作为参数,但你自己的处理函数不需要这个事件对象。

import functools
# 模拟一个按钮点击事件处理器
def button_click_handler(command, event):
    print(f"执行命令: {command}")
    # event 对象在这里被忽略了
# 创建一个处理 "save" 命令的回调函数
# 当按钮被点击时,它会自动调用 save_click_handler(event)
# 我们用 partial 预先填充了 command 参数
save_click_handler = functools.partial(button_click_handler, command="save")
# 模拟按钮被点击
# event = some_event_object
save_click_handler(event=None) # 输出: 执行命令: save

partial 可以让你的代码更简洁,避免重复传递相同的参数,特别是在处理回调函数时非常有用。


functools.wraps - 装饰器辅助函数

当你自己写一个装饰器时,原始函数的元信息(如函数名 __name__、文档字符串 __doc__ 等)会丢失。wraps 可以帮助你将这些元信息“复制”到装饰器内部的函数上。

问题示例:

def my_decorator(func):
    def wrapper(*args, **kwargs):
        """Wrapper function's docstring."""
        print("函数执行前...")
        result = func(*args, **kwargs)
        print("函数执行后...")
        return result
    return wrapper
@my_decorator
def example_function():
    """This is the docstring for example_function."""
    print("我是 example_function")
print(example_function.__name__) # 输出: wrapper
print(example_function.__doc__)  # 输出: Wrapper function's docstring.

可以看到,example_function 的元信息被 wrapper 函数覆盖了,这对于调试和代码文档化非常不利。

使用 wraps 解决问题:

from functools import wraps
def my_decorator(func):
    @wraps(func)  # 使用 wraps 装饰器
    def wrapper(*args, **kwargs):
        """Wrapper function's docstring."""
        print("函数执行前...")
        result = func(*args, **kwargs)
        print("函数执行后...")
        return result
    return wrapper
@my_decorator
def example_function():
    """This is the docstring for example_function."""
    print("我是 example_function")
print(example_function.__name__) # 输出: example_function
print(example_function.__doc__)  # 输出: This is the docstring for example_function.

在编写任何装饰器时,都应该在内部函数上使用 @wraps(func),这是一个好习惯,能让你的代码更易于维护和调试。


functools.lru_cache - 缓存装饰器

lru_cache 是一个非常实用的装饰器,它可以为函数提供缓存(Memoization)功能,它会记住最近调用过的参数和对应的返回结果,当再次使用相同的参数调用该函数时,它会直接从缓存中返回结果,而无需重新计算。

LRU 的意思是 Least Recently Used (最近最少使用),当缓存满时,它会淘汰最久未被使用的数据。

语法: @functools.lru_cache(maxsize=None, typed=False)

  • maxsize: 缓存的大小,如果设置为 None,则缓存大小无限制,如果设置为 0,则禁用缓存,推荐设置为 2 的幂次方(如 128, 256)。
  • typed: 如果设置为 True,会将不同类型的参数区分开(10 会被视为不同参数)。

示例:计算斐波那契数列

这是一个经典的例子,因为它包含大量的重复计算。

from functools import lru_cache
import time
# 不使用缓存
def fibonacci_no_cache(n):
    if n < 2:
        return n
    return fibonacci_no_cache(n-1) + fibonacci_no_cache(n-2)
# 使用缓存
@lru_cache(maxsize=128)
def fibonacci_with_cache(n):
    if n < 2:
        return n
    return fibonacci_with_cache(n-1) + fibonacci_with_cache(n-2)
# 性能对比
start_time = time.time()
fibonacci_no_cache(35)
end_time = time.time()
print(f"不使用缓存耗时: {end_time - start_time:.4f} 秒")
start_time = time.time()
fibonacci_with_cache(35)
end_time = time.time()
print(f"使用缓存耗时: {end_time - start_time:.4f} 秒")
# 查看缓存信息
print(fibonacci_with_cache.cache_info())
# 输出: CacheInfo(hits=34, misses=36, maxsize=128, currsize=36)
# hits: 缓存命中次数
# misses: 未命中次数(即实际计算的次数)

从结果可以看出,使用 lru_cache 后,性能提升是巨大的。

lru_cache 是优化纯函数(相同输入总是产生相同输出)性能的利器,特别适用于计算密集型且存在重复调用的函数。


functools.reduce - 归约函数

reduce 函数用于对一个序列(如列表、元组)中的所有元素进行累积操作,最终将整个序列“归约”为一个单一的值。

语法: functools.reduce(function, iterable[, initializer])

  • function: 一个二元函数,接受两个参数,并返回一个结果。
  • iterable: 一个可迭代对象。
  • initializer (可选): 初始值,如果提供,计算会从初始值开始;否则,序列的第一个元素会被用作初始值。

示例 1:计算列表所有元素的和

from functools import reduce
numbers = [1, 2, 3, 4, 5]
# reduce 会这样执行:
# (((1 + 2) + 3) + 4) + 5
total = reduce(lambda x, y: x + y, numbers)
print(total)  # 输出: 15
# 使用初始值 10
total_with_init = reduce(lambda x, y: x + y, numbers, 10)
print(total_with_init) # 输出: 25 (10 + 1 + 2 + 3 + 4 + 5)

示例 2:查找列表中的最大值

from functools import reduce
numbers = [10, 50, 2, 100, 35]
# reduce 会这样执行:
# max(max(max(max(10, 50), 2), 100), 35)
max_value = reduce(lambda x, y: x if x > y else y, numbers)
print(max_value)  # 输出: 100

sum()max() 的对比: 对于像求和、求最大值、求最小值这类常见操作,Python 内置的 sum()max()min() 函数通常更直观、更高效。reduce 更通用,可以处理任何累积逻辑。

reduce 是一个强大的工具,用于将一个序列“折叠”成一个值,虽然很多场景下有更简单的替代方案,但当你需要一个通用的累积逻辑时,reduce 是不二之选。


functools.singledispatch - 泛函数

Python 3.4+ 引入了 singledispatch,它允许你根据函数第一个参数的类型来分发到不同的实现上,这被称为单分派

这在不修改原有函数的情况下,为不同的数据类型实现不同的逻辑非常有用。

语法:

@functools.singledispatch
def func(arg):
    # 默认实现
    pass
@func.register
def _(arg: int):
    # 针对 int 类型的实现
    pass
@func.register
def _(arg: list):
    # 针对 list 类型的实现
    pass

注意: 注册函数的函数名不重要,通常使用 _

示例:为不同类型的数据实现“漂亮打印”

from functools import singledispatch
@singledispatch
def pretty_print(obj):
    """默认的打印方式"""
    print(f"默认打印: {repr(obj)}")
@pretty_print.register
def _(obj: int):
    """为整数类型定制的打印方式"""
    print(f"这是一个整数: {obj}")
@pretty_print.register
def _(obj: list):
    """为列表类型定制的打印方式"""
    print(f"这是一个列表,包含 {len(obj)} 个元素: {obj}")
@pretty_print.register
def _(obj: str):
    """为字符串类型定制的打印方式"""
    print(f"这是一个字符串,长度为 {len(obj)}: '{obj}'")
# 调用
pretty_print(123)        # 输出: 这是一个整数: 123
pretty_print([1, 2, 3])  # 输出: 这是一个列表,包含 3 个元素: [1, 2, 3]
pretty_print("hello")    # 输出: 这是一个字符串,长度为 5: 'hello'
pretty_print({"a": 1})   # 输出: 默认打印: {'a': 1}

singledispatch 是实现多态的一种优雅方式,特别适合在库或框架中为不同数据类型扩展功能,而无需创建复杂的继承体系。


其他重要成员

  • functools.total_ordering: 这是一个类装饰器,如果你在一个类中定义了 __eq____lt__ (或其他比较方法中的一个),@total_ordering 会自动为你填充剩下的所有比较方法 (__le__, __gt__, __ge__),让你免于编写重复的代码。
函数/类 用途 典型场景
partial 固定函数的部分参数,创建新函数。 简化回调函数、创建特定功能的函数。
wraps 保留原始函数的元信息 (__name__, __doc__ 等)。 编写任何装饰器时的必备工具。
lru_cache 缓存函数结果,避免重复计算。 优化纯函数性能,特别是递归或计算密集型函数。
reduce 将序列归约为单一值。 自定义累积逻辑(虽然常见操作有内置函数替代)。
singledispatch 根据第一个参数的类型分发到不同函数。 为不同数据类型扩展函数功能,实现多态。

functools 模块是 Python 高级编程的瑞士军刀,熟练掌握它能让你的代码更加优雅、高效和富有表现力。

分享:
扫描分享到社交APP
上一篇
下一篇