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()
执行流程:
@simple_decorator
等价于say_hello = simple_decorator(say_hello)
- 调用
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>
六、练习题
-
实现LRU缓存装饰器,限制最大缓存条目
-
编写重试装饰器,支持设置重试次数和延迟时间
-
创建类型检查装饰器,验证函数参数类型
-
实现单例模式类装饰器
-
开发路由注册装饰器(模拟Flask路由系统)
-
设计并发限制装饰器(控制同时执行线程数)
-
编写日志记录装饰器,记录函数调用参数和返回值
-
实现自动注册所有派生类的基类装饰器
-
创建异步函数超时控制装饰器
-
开发装饰器调试工具,打印完整调用堆栈
源码在博文顶部点击下载
最佳实践建议:
- 优先使用functools.wraps保持函数元信息
- 避免在装饰器中修改原始函数签名
- 谨慎处理装饰器中的状态保持
- 使用类装饰器处理复杂状态管理
- 通过单元测试验证装饰器行为
通过系统学习+实践练习,开发者可充分运用装饰器实现关注点分离、代码复用等目标,写出更优雅、可维护的Python代码。
作者:wolf犭良