第77集:装饰器基础

学习目标

  • 理解装饰器的概念和作用
  • 掌握装饰器的基本语法
  • 学会创建简单的装饰器
  • 理解装饰器的执行流程
  • 掌握带参数的装饰器
  • 了解装饰器的应用场景

什么是装饰器

装饰器(Decorator)是Python中一种特殊的语法,用于在不修改原函数代码的情况下,为函数添加额外的功能。装饰器本质上是一个接受函数作为参数并返回新函数的函数。

装饰器的特点

  • 不修改原函数代码
  • 不改变原函数调用方式
  • 可以添加额外功能
  • 可以组合使用多个装饰器
  • 支持带参数的装饰器

基本语法

语法结构

@decorator
def function():
    pass

等价形式

def function():
    pass

function = decorator(function)

基本示例

示例1:简单装饰器

def my_decorator(func):
    """简单装饰器"""
    def wrapper():
        print("函数执行前")
        func()
        print("函数执行后")
    return wrapper

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

# 调用函数
say_hello()
# 输出:
# 函数执行前
# Hello!
# 函数执行后

示例2:装饰器执行流程

def decorator(func):
    print("1. 装饰器被调用")
    
    def wrapper():
        print("3. wrapper函数被调用")
        func()
        print("5. 函数执行完成")
    
    print("2. wrapper函数被创建")
    return wrapper

@decorator
def my_function():
    print("4. 原函数执行")

print("装饰器应用完成")
my_function()

示例3:保留原函数信息

def my_decorator(func):
    """保留原函数信息的装饰器"""
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    wrapper.__name__ = func.__name__
    wrapper.__doc__ = func.__doc__
    return wrapper

@my_decorator
def add(a, b):
    """加法函数"""
    return a + b

print(add.__name__)  # add
print(add.__doc__)  # 加法函数

常用装饰器模式

模式1:计时装饰器

import time

def timer(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)
    print("函数执行中...")

slow_function()

模式2:日志装饰器

def logger(func):
    """日志装饰器"""
    def wrapper(*args, **kwargs):
        print(f"调用函数: {func.__name__}")
        print(f"参数: args={args}, kwargs={kwargs}")
        result = func(*args, **kwargs)
        print(f"返回值: {result}")
        return result
    return wrapper

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

add(3, 5)

模式3:缓存装饰器

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 <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))

带参数的装饰器

示例4:带参数的装饰器

def repeat(times):
    """重复执行装饰器"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            for _ in range(times):
                func(*args, **kwargs)
        return wrapper
    return decorator

@repeat(3)
def say_hello():
    print("Hello!")

say_hello()
# 输出3次Hello!

示例5:带参数的日志装饰器

def log(prefix=""):
    """带前缀的日志装饰器"""
    def decorator(func):
        def wrapper(*args, **kwargs):
            print(f"{prefix} 调用函数: {func.__name__}")
            result = func(*args, **kwargs)
            print(f"{prefix} 返回值: {result}")
            return result
        return wrapper
    return decorator

@log("DEBUG:")
def add(a, b):
    return a + b

add(3, 5)

多个装饰器

示例6:多个装饰器组合

def decorator1(func):
    def wrapper():
        print("装饰器1 - 前")
        func()
        print("装饰器1 - 后")
    return wrapper

def decorator2(func):
    def wrapper():
        print("装饰器2 - 前")
        func()
        print("装饰器2 - 后")
    return wrapper

@decorator1
@decorator2
def my_function():
    print("原函数")

my_function()
# 输出:
# 装饰器1 - 前
# 装饰器2 - 前
# 原函数
# 装饰器2 - 后
# 装饰器1 - 后

装饰器的应用场景

场景1:权限验证

def login_required(func):
    """登录验证装饰器"""
    def wrapper(*args, **kwargs):
        if not is_logged_in():
            raise PermissionError("需要登录")
        return func(*args, **kwargs)
    return wrapper

@login_required
def delete_user(user_id):
    print(f"删除用户: {user_id}")

def is_logged_in():
    return True

delete_user(123)

场景2:输入验证

def validate_positive(func):
    """验证参数为正数"""
    def wrapper(*args, **kwargs):
        for arg in args:
            if not isinstance(arg, (int, float)) or arg <= 0:
                raise ValueError("参数必须为正数")
        return func(*args, **kwargs)
    return wrapper

@validate_positive
def calculate_area(length, width):
    return length * width

print(calculate_area(5, 3))

场景3:性能监控

def performance_monitor(func):
    """性能监控装饰器"""
    def wrapper(*args, **kwargs):
        import time
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} 执行时间: {end-start:.4f}秒")
        return result
    return wrapper

@performance_monitor
def process_data(data):
    total = sum(data)
    return total

process_data(range(1000000))

functools.wraps

使用functools.wraps

from functools import wraps

def my_decorator(func):
    """使用wraps保留原函数信息"""
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def add(a, b):
    """加法函数"""
    return a + b

print(add.__name__)  # add
print(add.__doc__)  # 加法函数

为什么使用functools.wraps

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

@decorator_without_wraps
def func1():
    """函数1"""
    pass

print(func1.__name__)  # wrapper
print(func1.__doc__)  # None

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

@decorator_with_wraps
def func2():
    """函数2"""
    pass

print(func2.__name__)  # func2
print(func2.__doc__)  # 函数2

装饰器的限制

限制1:修改原函数签名

def my_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

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

# 装饰器后,函数签名可能改变
import inspect
print(inspect.signature(add))  # (*args, **kwargs)

限制2:调试困难

# 装饰器可能使调试变得困难
def my_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@my_decorator
def my_function():
    pass

# 堆栈跟踪可能显示wrapper而不是my_function

最佳实践

实践1:使用functools.wraps

from functools import wraps

def my_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

实践2:保持装饰器简单

# 推荐:简单的装饰器
def simple_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

# 不推荐:复杂的装饰器
def complex_decorator(func):
    def wrapper(*args, **kwargs):
        # 大量逻辑
        pass
    return wrapper

实践3:使用类装饰器处理复杂逻辑

class MyDecorator:
    """类装饰器"""
    def __init__(self, func):
        self.func = func
        wraps(func)(self)
    
    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)

@MyDecorator
def my_function():
    pass

实践4:文档化装饰器

def my_decorator(func):
    """
    装饰器文档
    
    Args:
        func: 被装饰的函数
    
    Returns:
        包装后的函数
    """
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

常见错误

错误1:忘记返回wrapper函数

# 错误:忘记返回wrapper函数
def bad_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    # 忘记return wrapper

# 正确:返回wrapper函数
def good_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

错误2:忘记使用*args和**kwargs

# 错误:不接受参数
def bad_decorator(func):
    def wrapper():
        return func()
    return wrapper

# 正确:接受任意参数
def good_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

错误3:忘记调用原函数

# 错误:忘记调用原函数
def bad_decorator(func):
    def wrapper(*args, **kwargs):
        # 忘记调用func()
        pass
    return wrapper

# 正确:调用原函数
def good_decorator(func):
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

总结

装饰器是Python中强大的工具,具有以下特点:

核心概念

  • 装饰器是一个接受函数并返回新函数的函数
  • 使用@语法糖简化装饰器的应用
  • 可以在不修改原函数代码的情况下添加功能

优势

  • 代码复用
  • 关注点分离
  • 提高代码可读性
  • 易于维护

应用场景

  • 日志记录
  • 性能监控
  • 权限验证
  • 缓存
  • 输入验证

掌握装饰器将帮助你编写更优雅、更可维护的Python代码。装饰器是Python中重要的概念,理解它的工作原理对于编写高质量的Python程序至关重要。

« 上一篇 生成器函数 下一篇 » 带参数装饰器