什么是装饰器及其核心概念

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

装饰器的核心价值

  1. 代码复用:将通用功能(如日志记录、性能测试、权限验证)封装成装饰器,可以在多个函数中重复使用
  2. 关注点分离:将核心业务逻辑与辅助功能(如日志、缓存)分离,提高代码可读性
  3. 非侵入式扩展:无需修改原函数代码即可添加新功能

基本装饰器示例

def logger(func):
    """一个简单的日志装饰器"""
    def wrapper(*args, **kwargs):
        print(f"开始执行函数: {func.__name__}")
        result = func(*args, **kwargs)
        print(f"函数执行完成: {func.__name__}")
        return result
    return wrapper

@logger
def add(a, b):
    return a + b

# 调用示例
print(add(3, 5))

输出结果:

开始执行函数: add
函数执行完成: add
8

装饰器的进阶用法

带参数的装饰器

有时我们需要为装饰器本身传递参数,这时需要创建三层嵌套函数:

def repeat(num_times):
    """重复执行函数的装饰器"""
    def decorator_repeat(func):
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator_repeat

@repeat(num_times=3)
def greet(name):
    print(f"Hello, {name}!")

# 调用示例
greet("Alice")

输出结果:

Hello, Alice!
Hello, Alice!
Hello, Alice!

类装饰器

Python还支持使用类作为装饰器,这通常在需要维护状态时很有用:

class CountCalls:
    """统计函数调用次数的类装饰器"""
    def __init__(self, func):
        self.func = func
        self.num_calls = 0

    def __call__(self, *args, **kwargs):
        self.num_calls += 1
        print(f"调用次数: {self.num_calls}")
        return self.func(*args, **kwargs)

@CountCalls
def say_hello():
    print("Hello!")

# 调用示例
say_hello()
say_hello()

输出结果:

调用次数: 1
Hello!
调用次数: 2
Hello!

实际应用场景

1. 性能监控装饰器

import time
import functools

def timer(func):
    """测量函数执行时间的装饰器"""
    @functools.wraps(func)  # 保留原函数的元信息
    def wrapper(*args, **kwargs):
        start_time = time.perf_counter()
        result = func(*args, **kwargs)
        end_time = time.perf_counter()
        print(f"{func.__name__} 执行时间: {end_time - start_time:.4f}秒")
        return result
    return wrapper

@timer
def slow_function():
    time.sleep(1)

# 调用示例
slow_function()

2. 缓存装饰器(记忆化)

def memoize(func):
    """缓存函数结果的装饰器"""
    cache = {}
    @functools.wraps(func)
    def wrapper(*args):
        if args in cache:
            return cache[args]
        result = func(*args)
        cache[args] = result
        return result
    return wrapper

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

# 调用示例(会显著提高递归效率)
print(fibonacci(30))  # 快速计算

3. 权限验证装饰器

def requires_role(role):
    """基于角色的访问控制装饰器"""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(user, *args, **kwargs):
            if user.get('role') != role:
                raise PermissionError(f"需要{role}权限")
            return func(user, *args, **kwargs)
        return wrapper
    return decorator

@requires_role('admin')
def delete_user(user):
    print(f"用户{user['name']}已被删除")

# 调用示例
admin = {'name': 'Alice', 'role': 'admin'}
user = {'name': 'Bob', 'role': 'user'}

delete_user(admin)  # 成功执行
# delete_user(user)  # 会抛出PermissionError

装饰器的注意事项

1. 使用functools.wraps

在编写装饰器时,应该始终使用@functools.wraps装饰内部包装函数,这样可以保留原函数的元信息(如函数名、文档字符串等):

import functools

def bad_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper  # 不使用wraps

def good_decorator(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper  # 使用wraps

@bad_decorator
def func1():
    """原始文档字符串"""
    pass

@good_decorator
def func2():
    """原始文档字符串"""
    pass

print(func1.__name__)  # 输出: wrapper
print(func1.__doc__)   # 输出: None

print(func2.__name__)  # 输出: func2
print(func2.__doc__)   # 输出: 原始文档字符串

2. 装饰器执行顺序

当多个装饰器堆叠时,它们的执行顺序是从下往上的:

def decorator1(func):
    print("装饰器1 - 装饰")
    def wrapper(*args, **kwargs):
        print("装饰器1 - 执行前")
        result = func(*args, **kwargs)
        print("装饰器1 - 执行后")
        return result
    return wrapper

def decorator2(func):
    print("装饰器2 - 装饰")
    def wrapper(*args, **kwargs):
        print("装饰器2 - 执行前")
        result = func(*args, **kwargs)
        print("装饰器2 - 执行后")
        return result
    return wrapper

@decorator1
@decorator2
def my_func():
    print("原函数执行")

# 调用
my_func()

输出顺序:

装饰器2 - 装饰
装饰器1 - 装饰
装饰器1 - 执行前
装饰器2 - 执行前
原函数执行
装饰器2 - 执行后
装饰器1 - 执行后

高级技巧:类方法装饰器

1. 实例方法装饰器

def method_decorator(func):
    @functools.wraps(func)
    def wrapper(self, *args, **kwargs):
        print(f"调用方法: {func.__name__}")
        return func(self, *args, **kwargs)
    return wrapper

class Calculator:
    @method_decorator
    def add(self, a, b):
        return a + b

calc = Calculator()
print(calc.add(2, 3))

2. 静态方法和类方法装饰器

class MyClass:
    @staticmethod
    @staticmethod_decorator  # 注意顺序:装饰器从下往上应用
    def static_method():
        return "静态方法"

    @classmethod
    @classmethod_decorator
    def class_method(cls):
        return f"类方法: {cls.__name__}"

装饰器库推荐

Python社区有许多现成的装饰器库可以节省开发时间:

  1. functools:Python标准库,提供lru_cache等实用装饰器
  2. wrapt:功能强大的装饰器库,处理复杂场景
  3. decorator:简化装饰器创建的库
  4. click:命令行工具库,提供丰富的装饰器

functools.lru_cache示例

from functools import lru_cache

@lru_cache(maxsize=128)
def expensive_calculation(n):
    print(f"计算{n}")
    return n * n

# 第一次调用会执行计算
print(expensive_calculation(4))  # 输出: 计算4 → 16

# 第二次调用直接返回缓存结果
print(expensive_calculation(4))  # 输出: 16

总结

装饰器是Python中非常强大且优雅的特性,掌握它们可以:

  1. 大幅减少重复代码
  2. 提高代码的可维护性
  3. 实现更清晰的关注点分离
  4. 构建更灵活的架构

关键要点:

  • 始终使用functools.wraps保留原函数元信息
  • 理解装饰器的执行顺序
  • 根据场景选择合适的装饰器类型(函数/类)
  • 考虑使用现成的装饰器库

通过合理使用装饰器,你可以让Python代码更加简洁、高效和专业。