Python functools模块实用教程

Python functools模块实用教程

functools模块是Python标准库中用于高阶函数操作的模块,提供了一系列用于函数操作的工具,包括函数装饰器、部分函数应用、函数属性管理等。这些工具可以帮助开发者编写更简洁、更高效的函数式Python代码。

1. 函数装饰器

1.1 @functools.cached_property (Python 3.8+)

将方法转换为缓存的属性,只计算一次,然后作为实例的常规属性缓存:

import functools

class Data:
    def __init__(self, n):
        self.n = n
    
    @functools.cached_property
    def expensive_computation(self):
        print("Computing...")
        return self.n * 2

d = Data(10)
print(d.expensive_computation)  # Computing... 20
print(d.expensive_computation)  # 20 (不再计算)

1.2 @functools.lru_cache(maxsize=128, typed=False)

函数装饰器,提供最近最少使用(LRU)缓存:

@functools.lru_cache(maxsize=32)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

print(fib(30))  # 第一次计算
print(fib(30))  # 从缓存中获取

# 查看缓存信息
print(fib.cache_info())  # CacheInfo(hits=28, misses=31, maxsize=32, currsize=31)

1.3 @functools.total_ordering

类装饰器,自动填充缺失的比较方法:

@functools.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

a = Student('Alice', 85)
b = Student('Bob', 90)
print(a <= b)  # True
print(a > b)   # False

2. 函数操作

2.1 functools.partial(func, /, *args, **keywords)

部分函数应用,固定函数的部分参数:

def power(base, exponent):
    return base ** exponent

square = functools.partial(power, exponent=2)
cube = functools.partial(power, exponent=3)

print(square(5))  # 25
print(cube(5))    # 125

2.2 functools.partialmethod(func, /, *args, **keywords)

类似于partial,但用于类方法:

class Cell:
    def __init__(self):
        self.alive = False
    
    def set_state(self, state):
        self.alive = state
    
    set_alive = functools.partialmethod(set_state, True)
    set_dead = functools.partialmethod(set_state, False)

cell = Cell()
cell.set_alive()
print(cell.alive)  # True

2.3 functools.reduce(function, iterable[, initializer])

将两个参数的函数从左到右累积地应用到iterable的项:

numbers = [1, 2, 3, 4, 5]
product = functools.reduce(lambda x, y: x * y, numbers)
print(product)  # 120 (1*2*3*4*5)

# 带初始值
sum_sq = functools.reduce(lambda x, y: x + y**2, numbers, 0)
print(sum_sq)  # 55 (0 + 1 + 4 + 9 + 16 + 25)

2.4 functools.singledispatch

将函数转换为单分派泛函数:

@functools.singledispatch
def process(data):
    raise NotImplementedError("Unsupported type")

@process.register(str)
def _(data):
    return f"Processing string: {data}"

@process.register(int)
def _(data):
    return f"Processing integer: {data}"

print(process("hello"))  # Processing string: hello
print(process(42))       # Processing integer: 42
print(process([]))       # NotImplementedError

2.5 functools.singledispatchmethod (Python 3.8+)

类似于singledispatch,但用于类方法:

class Processor:
    @functools.singledispatchmethod
    def process(self, data):
        raise NotImplementedError("Unsupported type")
    
    @process.register(str)
    def _(self, data):
        return f"Processing string: {data}"
    
    @process.register(int)
    def _(self, data):
        return f"Processing integer: {data}"

p = Processor()
print(p.process("hello"))  # Processing string: hello
print(p.process(42))       # Processing integer: 42

3. 函数包装与属性管理

3.1 functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

更新包装函数,使其看起来像被包装的函数:

def decorator(func):
    @functools.wraps(func)  # 等同于手动调用update_wrapper
    def wrapper(*args, **kwargs):
        """Wrapper docstring"""
        print("Before call")
        result = func(*args, **kwargs)
        print("After call")
        return result
    return wrapper

@decorator
def example():
    """Example docstring"""
    print("Inside example")

print(example.__name__)  # 'example' (而不是'wrapper')
print(example.__doc__)   # 'Example docstring' (而不是'Wrapper docstring')

3.2 @functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

装饰器工厂,用于调用update_wrapper的便捷方式:

def my_decorator(f):
    @functools.wraps(f)
    def wrapper(*args, **kwargs):
        print(f"Calling {f.__name__}")
        return f(*args, **kwargs)
    return wrapper

@my_decorator
def say_hello(name):
    """Greet someone"""
    return f"Hello, {name}!"

print(say_hello.__name__)  # 'say_hello'
print(say_hello.__doc__)   # 'Greet someone'

4. 高级函数组合

4.1 functools.compose(*functions) (Python中没有内置,但可以自己实现)

虽然Python标准库中没有直接提供compose函数,但可以用reduce实现:

def compose(*funcs):
    return functools.reduce(lambda f, g: lambda x: f(g(x)), funcs)

def add_one(x):
    return x + 1

def square(x):
    return x * x

add_one_and_square = compose(square, add_one)
print(add_one_and_square(2))  # 9 ((2+1)^2)

5. 实用示例

5.1 带缓存的API调用

@functools.lru_cache(maxsize=100)
def get_user_info(user_id):
    print(f"Fetching user {user_id} from API")
    # 模拟API调用
    return {"id": user_id, "name": f"User {user_id}"}

print(get_user_info(1))  # Fetching user 1 from API
print(get_user_info(1))  # 从缓存获取

5.2 带超时的缓存

import time

def timed_lru_cache(seconds, maxsize=128):
    def wrapper(func):
        func = functools.lru_cache(maxsize=maxsize)(func)
        func.lifetime = seconds
        func.expiration = time.monotonic() + seconds
        
        @functools.wraps(func)
        def wrapped(*args, **kwargs):
            if time.monotonic() >= func.expiration:
                func.cache_clear()
                func.expiration = time.monotonic() + func.lifetime
            return func(*args, **kwargs)
        return wrapped
    return wrapper

@timed_lru_cache(seconds=10)
def get_data():
    print("Fetching fresh data")
    return time.time()

print(get_data())  # Fetching fresh data
print(get_data())  # 从缓存获取 (10秒内)

5.3 带重试机制的装饰器

def retry(max_attempts=3, delay=1):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            attempts = 0
            while attempts < max_attempts:
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    attempts += 1
                    if attempts == max_attempts:
                        raise
                    time.sleep(delay)
        return wrapper
    return decorator

@retry(max_attempts=3, delay=1)
def unreliable_api_call():
    if random.random() < 0.7:
        raise ValueError("API failed")
    return "Success"

6. 注意事项

  1. lru_cache:对于计算密集型函数,缓存可以显著提高性能
  2. partial:可以减少函数调用时的参数传递
  3. singledispatch:比一系列if/elif/else更高效且更易维护
  4. wraps:保持函数元信息对于调试和文档很重要

7. 总结

functools模块提供了:

  • 函数缓存lru_cache, cached_property
  • 部分函数应用partial, partialmethod
  • 函数组合与装饰reduce, wraps, update_wrapper
  • 多态分发singledispatch, singledispatchmethod
  • 类工具total_ordering

通过合理使用functools模块具有如下优势:

  1. 编写更高效的函数式代码
  2. 减少重复计算
  3. 创建更灵活的API
  4. 保持代码的整洁和可维护性
  5. 实现更优雅的函数组合和装饰

你可能感兴趣的:(python内置模块,python,开发语言)