18、Python 装饰器原理与高级用法从入门到实战

Python 装饰器原理与高级用法从入门到实战

[导读] 装饰器(Decorator)是Python中最优雅的语法糖之一,也是进阶Python开发的必经之路。本文将深入剖析装饰器执行机制,详解@wraps的作用原理,手把手实现带参数装饰器与类装饰器,并通过5个典型实战案例展示装饰器在权限控制、性能优化等场景的应用。最后提供10道梯度练习题帮助读者全面掌握这一重要特性。


一、装饰器前置知识

1.1 函数是一等公民

Python中函数可作为参数传递、作为返回值、赋值给变量:

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

# 函数赋值
say_hi = greet
print(say_hi("Alice"))  # Hello, Alice!

# 函数作为参数
def call_func(func, arg):
    return func(arg)
    
print(call_func(greet, "Bob"))  # Hello, Bob!

1.2 闭包(Closure)

内部函数捕获外部作用域变量的机制:

def outer(msg):
    def inner():
        print(f"Message: {msg}")  # 捕获外部变量msg
    return inner

closure = outer("Closure works!")
closure()  # Message: Closure works!

关键点:闭包会持有外部变量的引用,可能导致变量值延迟绑定问题


二、装饰器核心原理

2.1 基础装饰器实现

def simple_decorator(func):
    def wrapper():
        print("Before function call")
        func()
        print("After function call")
    return wrapper

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

say_hello()

执行流程

  1. @simple_decorator等价于 say_hello = simple_decorator(say_hello)
  2. 调用say_hello()实际执行wrapper()

2.2 使用@wraps保持元信息

from functools import wraps

def keep_metadata(func):
    @wraps(func)  # 保留原始函数信息
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

@keep_metadata
def sample():
    """This is a docstring"""
    pass

print(sample.__name__)  # 输出'sample'(不使用@wraps会显示'wrapper')
print(sample.__doc__)   # 显示原始文档字符串

三、进阶装饰器实现

3.1 带参数的装饰器

def repeat(num_times):
    # 外层处理装饰器参数
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for _ in range(num_times):
                result = func(*args, **kwargs)
            return result
        return wrapper
    return decorator

@repeat(3)
def countdown(n):
    while n > 0:
        print(n)
        n -= 1

countdown(2)  # 打印2 1 各三次

3.2 类装饰器

class TraceCall:
    def __init__(self, func):
        self.func = func
        self.call_count = 0

    def __call__(self, *args, **kwargs):
        self.call_count += 1
        print(f"Call {self.call_count} to {self.func.__name__}")
        return self.func(*args, **kwargs)

@TraceCall
def compute(x, y):
    return x * y

compute(3, 4)  # 输出调用信息并返回12

四、实战场景案例

4.1 接口权限验证

def require_role(role):
    def decorator(func):
        @wraps(func)
        def wrapper(user, *args, **kwargs):
            if user.role != role:
                raise PermissionError(f"Requires {role} role")
            return func(user, *args, **kwargs)
        return wrapper
    return decorator

class User:
    def __init__(self, role):
        self.role = role

@require_role("admin")
def delete_database(user):
    print("Database deleted!")

admin = User("admin")
user = User("guest")
delete_database(admin)  # 正常执行
delete_database(user)   # 抛出PermissionError

4.2 函数执行时间统计

import time
from functools import wraps

def timeit(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.perf_counter()
        result = func(*args, **kwargs)
        elapsed = time.perf_counter() - start
        print(f"{func.__name__} took {elapsed:.4f} seconds")
        return result
    return wrapper

@timeit
def heavy_computation(n):
    return sum(i*i for i in range(n))

heavy_computation(10**6)  # 输出执行时间

五、装饰器堆叠与执行顺序

多个装饰器按从下往上的顺序应用:

@decorator1
@decorator2
def my_func():
    pass
# 等价于 my_func = decorator1(decorator2(my_func))

执行顺序演示

def add_tag(tag):
    def decorator(func):
        def wrapper():
            return f"<{tag}>{func()}</{tag}>"
        return wrapper
    return decorator

@add_tag("div")
@add_tag("strong")
def get_text():
    return "Hello World"

print(get_text())  
# 输出:<div><strong>Hello World</strong></div>

六、练习题

  1. 实现LRU缓存装饰器,限制最大缓存条目

  2. 编写重试装饰器,支持设置重试次数和延迟时间

  3. 创建类型检查装饰器,验证函数参数类型

  4. 实现单例模式类装饰器

  5. 开发路由注册装饰器(模拟Flask路由系统)

  6. 设计并发限制装饰器(控制同时执行线程数)

  7. 编写日志记录装饰器,记录函数调用参数和返回值

  8. 实现自动注册所有派生类的基类装饰器

  9. 创建异步函数超时控制装饰器

  10. 开发装饰器调试工具,打印完整调用堆栈

    源码在博文顶部点击下载


最佳实践建议

  1. 优先使用functools.wraps保持函数元信息
  2. 避免在装饰器中修改原始函数签名
  3. 谨慎处理装饰器中的状态保持
  4. 使用类装饰器处理复杂状态管理
  5. 通过单元测试验证装饰器行为

通过系统学习+实践练习,开发者可充分运用装饰器实现关注点分离、代码复用等目标,写出更优雅、可维护的Python代码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wolf犭良

谢谢您的阅读与鼓励!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值