Python中装饰器(Decorators)的实用指南:从基础到高阶应用

57 阅读3分钟

在Python开发中,装饰器是一种强大的工具,允许开发者在不修改原函数代码的前提下,动态扩展功能。这一知识点广泛应用于日志记录、权限验证、性能监控等场景,但因其语法抽象,常成为学习难点。本文将深入解析装饰器的核心机制、实际应用及常见陷阱,帮助你高效掌握这一高级特性。

一、装饰器的核心概念与语法

装饰器本质是‌接收函数作为参数并返回新函数‌的语法糖,通过@符号简化调用。其核心逻辑基于Python的一等函数特性(函数可被赋值、传递和返回)。

基础语法‌:

def decorator_func(func):  
    def wrapper(*args, **kwargs):  
        print("函数执行前操作")  
        result = func(*args, **kwargs)  
        print("函数执行后操作")  
        return result  
    return wrapper  

@decorator_func  
def greet(name):  
    return f"Hello, {name}!  

greet("Alice")  # 输出:  
# 函数执行前操作  
# Hello, Alice!  
# 函数执行后操作  

关键点‌:

  • wrapper函数必须与原函数参数兼容(使用*args, **kwargs)。

  • 装饰器会修改原函数的元信息(如__name__),可通过functools.wraps修复:

    from functools import wraps  
    def decorator_func(func):  
        @wraps(func)  
        def wrapper(*args, **kwargs):  
            # ...  
        return wrapper  
    

二、实际应用场景与代码示例

1. ‌日志记录‌:自动记录函数执行时间与参数。

python
    import time  
    from functools import wraps  
    def log_execution(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  

    @log_execution  
    def calculate_sum(n):  
        return sum(range(n))  

    calculate_sum(1000000)  # 输出: 函数 calculate_sum 耗时 0.1234秒  

1. ‌权限验证‌:检查用户权限后再执行函数。

    def requires_admin(func):  
        @wraps(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, user_id):  
        print(f"删除用户 {user_id}")  

    delete_user({"role": "user"}, 123)  # 抛出 PermissionError  

2. ‌缓存结果‌:避免重复计算(如斐波那契数列)。

    pythonCopy Code
    from functools import lru_cache  

    @lru_cache(maxsize=128)  
    def fib(n):  
        if n < 2:  
            return n  
        return fib(n-1) + fib(n-2)  

    print(fib(30))  # 首次计算后,后续调用直接返回缓存结果  

三、类装饰器与带参数的装饰器

1. ‌类装饰器‌:通过__call__方法实现。

  pythonCopy Code
    class CountCalls:  
        def __init__(self, func):  
            self.func = func  
            self.calls = 0  

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

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

    say_hello()  # 输出: 调用次数: 1  
    say_hello()  # 输出: 调用次数: 2  
1. ‌带参数的装饰器‌:需嵌套函数。
def repeat(times):  
        def decorator(func):  
            @wraps(func)  
            def wrapper(*args, **kwargs):  
                for _ in range(times):  
                    result = func(*args, **kwargs)  
                return result  
            return wrapper  
        return decorator  

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

    print(greet("Bob"))  # 输出: Hello, Bob! 重复3次  

四、常见误区与最佳实践

  • 误区1‌:忽略*args, **kwargs导致参数丢失。
    解决方案‌:始终在wrapper中保留参数传递。

  • 误区2‌:过度使用装饰器导致代码可读性下降。
    最佳实践‌:

    • 为装饰器添加文档说明。
    • 避免多层嵌套装饰器(如@decorator1 @decorator2)。
  • 性能注意‌:装饰器会增加函数调用开销,高频调用时需谨慎。