🐍 Python 核心技术

装饰器 Decorators

掌握 Python 中最优雅的代码复用模式,深入理解函数式编程的精髓

01

核心概念

📌 什么是装饰器?

装饰器(Decorator)是 Python 中一种强大的语法特性,它允许你在不修改原函数代码的情况下,动态地扩展函数的功能。装饰器本质上是一个接收函数作为参数并返回新函数的高阶函数。

装饰器使用 @ 符号作为语法糖,使代码更加简洁优雅。它是 Python 函数式编程和元编程的重要组成部分。

🎯 装饰器核心概念示意图
原函数 function() 装饰器 @decorator + 新增功能 增强函数 原功能 + 新功能 装饰器包装原函数,在不修改原代码的情况下添加新功能 遵循开放-封闭原则 (OCP)
🔧

代码复用

将通用功能抽象为装饰器,避免代码重复,提高可维护性

🎨

关注点分离

将横切关注点(如日志、权限)与业务逻辑解耦

动态增强

在运行时动态修改函数行为,无需改动源代码

📦

可组合性

多个装饰器可以叠加使用,灵活组合功能

02

工作原理

🔍 装饰器的本质

要理解装饰器,首先需要理解 Python 中函数是一等公民这一概念。函数可以:

  • 作为参数传递给其他函数
  • 作为其他函数的返回值
  • 赋值给变量
  • 存储在数据结构中
⚙️ 装饰器执行流程图
① 定义装饰器函数 ② 使用 @decorator 语法 ③ Python 自动执行转换 func = decorator(func) ④ 返回包装后的函数 关键步骤 调用被装饰的函数时,实际执行的是包装函数 wrapper wrapper 内部会调用原函数并添加额外逻辑

💡 核心理解

当你写 @decorator 放在函数定义上方时,Python 会自动将该函数作为参数传递给装饰器,并用装饰器的返回值替换原函数。这就是为什么装饰器必须返回一个可调用对象。

03

代码示例

🔰 示例1:基础装饰器 - 函数计时器

最基础的装饰器用法,用于测量函数执行时间:

Python
import time
from functools import wraps

# 定义一个计时装饰器
def timer(func):
    """测量函数执行时间的装饰器"""
    @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

# 使用装饰器
@timer
def slow_function():
    """模拟一个耗时操作"""
    time.sleep(1)
    return "完成!"

# 调用函数
result = slow_function()
print(f"返回值: {result}")
📤 输出结果:
函数 slow_function 执行耗时: 1.0012 秒
返回值: 完成!

⚡ 示例2:带参数的装饰器 - 重试机制

当装饰器本身需要接收参数时,需要再嵌套一层函数:

Python
import time
from functools import wraps

# 带参数的重试装饰器
def retry(max_attempts=3, delay=1):
    """
    自动重试装饰器
    :param max_attempts: 最大重试次数
    :param delay: 重试间隔(秒)
    """
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            attempts = 0
            while attempts < max_attempts:
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    attempts += 1
                    print(f"第 {attempts} 次尝试失败: {e}")
                    if attempts < max_attempts:
                        print(f"等待 {delay} 秒后重试...")
                        time.sleep(delay)
            raise Exception(f"函数 {func.__name__} 在 {max_attempts} 次尝试后仍然失败")
        return wrapper
    return decorator

# 模拟不稳定的网络请求
attempt_count = 0

@retry(max_attempts=3, delay=0.5)
def unstable_network_call():
    """模拟不稳定的网络请求"""
    global attempt_count
    attempt_count += 1
    if attempt_count < 3:
        raise ConnectionError("网络连接失败")
    return "请求成功!"

# 测试重试机制
result = unstable_network_call()
print(f"最终结果: {result}")
📤 输出结果:
第 1 次尝试失败: 网络连接失败
等待 0.5 秒后重试...
第 2 次尝试失败: 网络连接失败
等待 0.5 秒后重试...
最终结果: 请求成功!

🚀 示例3:类装饰器 - 缓存机制

使用类实现装饰器,可以维护状态,实现更复杂的功能:

Python
from functools import wraps

class Memoize:
    """
    缓存装饰器类
    缓存函数的计算结果,避免重复计算
    """
    def __init__(self, func):
        self.func = func
        self.cache = {}  # 存储缓存结果
        wraps(func)(self)  # 保留原函数元信息
    
    def __call__(self, *args):
        # 检查缓存中是否已有结果
        if args in self.cache:
            print(f"  → 缓存命中: {args}")
            return self.cache[args]
        
        # 计算结果并存入缓存
        print(f"  → 计算中: {args}")
        result = self.func(*args)
        self.cache[args] = result
        return result
    
    def clear_cache(self):
        """清空缓存"""
        self.cache.clear()

# 使用缓存装饰器优化递归计算
@Memoize
def fibonacci(n):
    """计算斐波那契数列第 n 项"""
    if n < 2:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)

# 测试缓存效果
print("计算 fibonacci(5):")
result = fibonacci(5)
print(f"结果: {result}\n")

print("再次计算 fibonacci(5):")
result = fibonacci(5)
print(f"结果: {result}")
📤 输出结果:
计算 fibonacci(5):
→ 计算中: (5,)
→ 计算中: (4,)
→ 计算中: (3,)
→ 计算中: (2,)
→ 计算中: (1,)
→ 计算中: (0,)
→ 缓存命中: (1,)
→ 缓存命中: (2,)
→ 缓存命中: (3,)
结果: 5

再次计算 fibonacci(5):
→ 缓存命中: (5,)
结果: 5
04

高级应用

🔗 多装饰器嵌套执行顺序
@decorator_a (外层) @decorator_b (中层) @decorator_c (内层) 原函数 func 执行顺序 ↓ 1. decorator_a 2. decorator_b 3. decorator_c 4. func() 装饰器从下往上应用,从外往内执行

🎯 常见应用场景

📝

日志记录

自动记录函数调用参数、返回值和执行时间

🔐

权限验证

在执行函数前检查用户权限,实现访问控制

💾

结果缓存

缓存函数计算结果,避免重复计算提升性能

参数验证

在函数执行前自动验证参数类型和范围

🔄

事务管理

数据库事务的自动提交和回滚处理

⏱️

限流控制

限制函数的调用频率,防止资源过度使用

05

最佳实践

✅ 使用要点

1

使用 @wraps

始终使用 functools.wraps 保留原函数的元信息(__name__, __doc__ 等)

2

支持所有参数

使用 *args 和 **kwargs 确保装饰器能处理任意参数的函数

3

保持简单

每个装饰器只做一件事,遵循单一职责原则

4

添加文档

为装饰器编写清晰的文档字符串说明其功能

⚠️ 常见陷阱

  • 忘记调用原函数或返回其结果
  • 带参数装饰器忘记额外的嵌套层
  • 装饰器副作用影响了函数的纯净性
  • 过度使用装饰器导致调试困难

💡 Python 标准库中的装饰器

@staticmethod - 定义静态方法
@classmethod - 定义类方法
@property - 将方法转为属性访问
@functools.lru_cache - 内置的缓存装饰器
@dataclasses.dataclass - 自动生成类方法