11 Python装饰器:代码功能扩展的利器

在 Python 中,装饰器(Decorator) 是一种特殊的函数,它允许你在不修改原有代码的情况下,扩展或修改其他函数的行为。装饰器本质上是一个高阶函数,它接受一个函数作为输入,并返回一个新的函数。

一、装饰器的基本概念

1. 装饰器的作用
  • 增强函数功能:例如添加日志记录、性能测试、权限验证等。
  • 代码复用:避免在多个函数中重复编写相同的代码。
  • 分离关注点:将核心业务逻辑与辅助功能(如日志、缓存)分离。
2. 简单装饰器示例
def logger(func):
    def wrapper(*args, **kwargs):
        print(f"[INFO] 调用函数: {func.__name__}")
        result = func(*args, **kwargs)  # 执行原函数
        print(f"[INFO] 函数返回: {result}")
        return result
    return wrapper

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

# 等价于 add = logger(add)
3. 装饰器的执行过程
  • 当使用 @logger 语法糖装饰 add 函数时,Python 会自动执行 add = logger(add)
  • logger 函数返回 wrapper 函数,因此 add 现在指向 wrapper
  • 调用 add(3, 5) 时,实际上调用的是 wrapper(3, 5),它会先打印日志,再执行原函数,最后打印返回结果。

二、装饰器的高级用法

1. 带参数的装饰器

如果装饰器需要接受额外参数(如日志级别、重试次数),可以定义一个“装饰器工厂”函数。

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

@repeat(3)  # 等价于 add = repeat(3)(add)
def greet(name):
    print(f"Hello, {name}!")
2. 多个装饰器叠加

多个装饰器可以叠加使用,执行顺序是从下到上(即靠近函数定义的装饰器先执行)。

def bold(func):
    def wrapper():
        return f"{func()}"
    return wrapper

def italic(func):
    def wrapper():
        return f"{func()}"
    return wrapper

@bold
@italic
def text():
    return "Hello"

print(text())  # 输出: Hello
3. 保留原函数元信息

使用 functools.wraps 装饰器保留原函数的名称、文档字符串等元信息。

import functools

def logger(func):
    @functools.wraps(func)  # 保留原函数元信息
    def wrapper(*args, **kwargs):
        print(f"调用函数: {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@logger
def add(a, b):
    """计算两数之和"""
    return a + b

print(add.__name__)  # 输出: add(而不是 wrapper)
print(add.__doc__)   # 输出: 计算两数之和
4. 类装饰器

除了函数装饰器,还可以使用类来实现装饰器。类装饰器需要实现 __call__ 方法。

class CountCalls:
    def __init__(self, func):
        self.func = func
        self.count = 0  # 记录调用次数

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

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

say_hello()  # 输出: 第 1 次调用 say_hello
say_hello()  # 输出: 第 2 次调用 say_hello

三、常见的装饰器应用场景

1. 缓存装饰器

使用 functools.lru_cache 缓存函数结果,避免重复计算。

import functools

@functools.lru_cache(maxsize=128)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)
2. 计时装饰器

测量函数执行时间。

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} 执行时间: {end - start:.4f} 秒")
        return result
    return wrapper

@timer
def process_data():
    time.sleep(1)
3. 权限验证装饰器

在执行函数前验证用户权限。

def requires_admin(func):
    def wrapper(user, *args, **kwargs):
        if user["role"] != "admin":
            raise PermissionError("需要管理员权限")
        return func(user, *args, **kwargs)
    return wrapper

@requires_admin
def delete_user(user, username):
    print(f"删除用户: {username}")

四、装饰器的注意事项

  1. 参数传递:装饰器的 wrapper 函数需要接受 *args**kwargs,以处理原函数的任意参数。
  2. 返回值处理wrapper 函数必须返回原函数的结果(即调用 func(*args, **kwargs))。
  3. 元信息丢失:如果不使用 functools.wraps,装饰后的函数元信息(如 __name____doc__)会被修改。
  4. 装饰器顺序:多个装饰器叠加时,执行顺序可能影响结果,需要谨慎设计。

你可能感兴趣的:(Python,python,开发语言)