如何堆叠多个装饰器

PythonBeginner
立即练习

简介

Python 装饰器是强大的工具,它允许开发者在不改变函数核心实现的情况下修改或增强函数。本教程将探讨堆叠多个装饰器的高级技术,展示如何通过分层不同的装饰器在 Python 编程中创建更复杂、更灵活的函数行为。

装饰器基础

什么是装饰器?

在 Python 中,装饰器是一种强大且灵活的方式,用于修改或增强函数或类,而无需直接更改其源代码。本质上,装饰器是将另一个函数作为参数,并返回该函数修改版本的函数。

基本装饰器语法

def my_decorator(func):
    def wrapper():
        print("函数调用前的一些操作。")
        func()
        print("函数调用后的一些操作。")
    return wrapper

@my_decorator
def say_hello():
    print("你好!")

say_hello()

装饰器的关键特性

函数包装

装饰器围绕现有函数进行包装,在原始函数执行之前或之后添加额外功能。

graph LR A[原始函数] --> B[装饰器包装器] B --> C[增强的功能]

装饰器类型

装饰器类型 描述 示例用例
函数装饰器 修改函数行为 日志记录、计时
类装饰器 修改类行为 单例模式
方法装饰器 修改方法行为 访问控制

实际示例:计时装饰器

import time

def timer_decorator(func):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"函数 {func.__name__} 执行耗时 {end_time - start_time} 秒。")
        return result
    return wrapper

@timer_decorator
def slow_function():
    time.sleep(2)
    print("慢速函数完成")

slow_function()

带参数的装饰器

def repeat(times):
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def greet(name):
    print(f"你好,{name}!")

greet("LabEx 用户")

常见用例

  1. 日志记录
  2. 性能测量
  3. 认证
  4. 缓存
  5. 输入验证

通过理解这些基础知识,你将为探索 Python 中更高级的装饰器技术做好充分准备。LabEx 建议通过实践这些概念来提高熟练度。

装饰器链

理解装饰器链

装饰器链允许将多个装饰器应用于单个函数。当使用多个装饰器时,它们从下往上应用。

graph TD A[原始函数] --> B[装饰器1] B --> C[装饰器2] C --> D[装饰器3] D --> E[最终增强的函数]

基本装饰器链示例

def decorator1(func):
    def wrapper(*args, **kwargs):
        print("装饰器1 - 函数调用前")
        result = func(*args, **kwargs)
        print("装饰器1 - 函数调用后")
        return result
    return wrapper

def decorator2(func):
    def wrapper(*args, **kwargs):
        print("装饰器2 - 函数调用前")
        result = func(*args, **kwargs)
        print("装饰器2 - 函数调用后")
        return result
    return wrapper

@decorator1
@decorator2
def example_function():
    print("在原始函数内部")

example_function()

实际的链式应用场景

日志记录和计时装饰器

import time
import functools

def log_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"调用函数: {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

def timer_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"函数耗时 {end_time - start_time:.4f} 秒")
        return result
    return wrapper

@log_decorator
@timer_decorator
def complex_calculation(n):
    return sum(i**2 for i in range(n))

complex_calculation(10000)

装饰器链的注意事项

注意事项 描述 影响
顺序很重要 装饰器从下往上应用 改变函数行为
性能 多个装饰器可能会增加开销 考虑复杂度
可读性 太多装饰器会降低代码清晰度 谨慎使用

带参数的高级链式应用

def validate_input(min_value=0, max_value=100):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            for arg in args:
                if not (min_value <= arg <= max_value):
                    raise ValueError(f"输入必须在 {min_value} 和 {max_value} 之间")
            return func(*args, **kwargs)
        return wrapper
    return decorator

@validate_input(min_value=10, max_value=50)
@log_decorator
def process_number(x):
    return x * 2

process_number(30)  ## 可行
## process_number(5)  ## 引发 ValueError

最佳实践

  1. 使用 functools.wraps 来保留函数元数据
  2. 使装饰器专注且单一用途
  3. 注意性能影响
  4. 记录复杂的装饰器链

LabEx 建议通过实践装饰器链来掌握这一强大的 Python 技术。

实际应用案例

认证与授权

def require_auth(role):
    def decorator(func):
        def wrapper(*args, **kwargs):
            ## 模拟认证检查
            current_user = {
                'username': 'admin',
                'role': 'admin'
            }
            if current_user['role'] == role:
                return func(*args, **kwargs)
            else:
                raise PermissionError("未经授权的访问")
        return wrapper
    return decorator

@require_auth('admin')
def delete_user(user_id):
    print(f"正在删除用户 {user_id}")

delete_user(123)  ## 管理员可操作

缓存机制

def memoize(func):
    cache = {}
    def wrapper(*args):
        if args not in cache:
            cache[args] = func(*args)
        return cache[args]
    return wrapper

@memoize
def fibonacci(n):
    if n < 2:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(100))  ## 高效计算

性能监控

import time
import functools

def performance_tracker(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        result = func(*args, **kwargs)
        end_time = time.time()
        print(f"{func.__name__} 执行耗时 {end_time - start_time:.4f} 秒")
        return result
    return wrapper

@performance_tracker
def heavy_computation(n):
    return sum(i**2 for i in range(n))

heavy_computation(1000000)

速率限制

from functools import wraps
import time

def rate_limit(max_calls, time_frame):
    calls = []
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            now = time.time()
            calls[:] = [c for c in calls if now - c < time_frame]

            if len(calls) >= max_calls:
                raise Exception("速率限制已超出")

            calls.append(now)
            return func(*args, **kwargs)
        return wrapper
    return decorator

@rate_limit(max_calls=3, time_frame=60)
def api_call():
    print("API 调用成功")

## 演示速率限制
for _ in range(4):
    try:
        api_call()
    except Exception as e:
        print(e)

日志记录与调试

import logging
import functools

def log_calls(logger=None):
    if logger is None:
        logger = logging.getLogger(__name__)

    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            logger.info(f"正在调用 {func.__name__}")
            try:
                result = func(*args, **kwargs)
                logger.info(f"{func.__name__} 成功完成")
                return result
            except Exception as e:
                logger.error(f"{func.__name__} 中的错误: {e}")
                raise
        return wrapper
    return decorator

@log_calls()
def divide(a, b):
    return a / b

divide(10, 2)
## divide(10, 0)  ## 会记录错误

用例比较

用例 主要目的 主要优点
认证 控制访问 安全性
缓存 优化性能 减少计算量
监控 跟踪性能 提供洞察
速率限制 防止滥用 资源保护
日志记录 跟踪函数调用 调试

高级装饰器模式

graph TD A[装饰器模式] --> B[函数式装饰器] A --> C[类装饰器] A --> D[参数化装饰器] A --> E[链式装饰器]

最佳实践

  1. 使装饰器功能单一
  2. 使用 functools.wraps 保留元数据
  3. 优雅地处理异常
  4. 考虑性能影响

LabEx 建议探索这些实际应用案例以提升你的 Python 编程技能。

总结

通过理解如何在 Python 中堆叠多个装饰器,开发者可以创建更具模块化、可复用性和复杂性的代码。这项技术能够实现精确的函数修改,轻松地组合不同的装饰器,并提供一种简洁、易读的方式来扩展函数功能,同时又不会损害原始函数的核心逻辑。