【Python 进阶】终极形态:带参装饰器与类装饰器的黑科技
·
专栏进度:03 / 03 (装饰器专题收官)
在前两篇中,我们学会了如何给函数“整容”。但现实业务往往更复杂:
场景 A:你写了一个权限校验装饰器,但有的接口需要 admin 权限,有的只需要 user 权限。
场景 B:你写了一个重试装饰器,但不同的接口需要不同的重试次数。
这种情况下,普通的装饰器就不够用了,我们需要带参数的装饰器。
一、 三层嵌套:带参数装饰器的逻辑套娃
要让装饰器接收参数,我们需要在原有“两层嵌套”的基础上,再包一层工厂函数。
实战:一个可以指定“重试次数”的装饰器
from functools import wraps
def retry(times):
"""最外层:负责接收参数"""
def decorator(func):
"""中间层:传统的装饰器"""
@wraps(func)
def wrapper(*args, **kwargs):
"""最内层:实际的逻辑劫持"""
for i in range(times):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"尝试第 {i+1} 次失败,原因: {e}")
return None
return wrapper
return decorator
@retry(times=3)
def fetch_data():
raise ConnectionError("网络波动")
fetch_data()
理解口诀:
外层传参(times)。
中层传函数(func)。
内层传数据(*args, **kwargs)。
二、 类装饰器:用“对象”管理复杂的装饰逻辑
当你的装饰器逻辑变得非常庞大(例如需要记录状态、缓存数据)时,三层嵌套会让代码阅读者感到痛苦。这时,类装饰器是更优雅的选择。
原理:利用类的 init 接收函数,利用 call 实现劫持。
实战:简单的单例模式装饰器
class Singleton:
def __init__(self, cls):
self._cls = cls
self._instance = None
def __call__(self, *args, **kwargs):
if self._instance is None:
self._instance = self._cls(*args, **kwargs)
return self._instance
@Singleton
class DatabaseConnection:
def __init__(self):
print("连接数据库...")
无论创建多少次,永远是同一个对象
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # True
提示:类装饰器非常适合用来做状态保持。比如统计某个接口在整个生命周期内被调用了多少次。
三、 避坑指南:带参数装饰器的“消失调用”
新手常犯的错误:
错误:@retry (漏掉了括号)
正确:@retry(3) 或 @retry()
如果你的装饰器设计为接收参数,那么即便你使用默认参数,也必须带上括号。因为 @retry() 返回的是真正的装饰器函数,而直接写 @retry 传递的是工厂函数本身。
四、 专题总结:逻辑劫持的架构哲学
从闭包的内存胶囊,到装饰器的优雅整容,再到带参/类装饰器的黑科技,我们完成了 Python 函数式编程的高级跨越。
核心心法:
解耦:将非业务逻辑(日志、性能、权限)抽离。
复用:一行 @ 代码实现千行代码的注入。
克制:不要为了用装饰器而写装饰器,保持代码的直观性。
更多推荐



所有评论(0)