Python 装饰器
一、什么是装饰器
装饰器本质:接收函数作为参数,返回一个新函数,在不修改原函数代码、不改变调用方式的前提下,给原函数增加额外功能。
常见用途:日志记录、性能计时、权限校验、缓存、参数校验、异常捕获等。
核心特点
- 不修改原函数内部代码
- 不改变原函数调用方式
- 可以给函数动态增加功能
二、最简单的装饰器入门
1. 基础版:无参装饰器
python
运行
# 定义装饰器
def timer(func):
# 内层包装函数
def wrapper():
import time
start = time.time()
func() # 执行原函数
end = time.time()
print(f"函数执行耗时:{end - start:.4f}秒")
return wrapper
# 使用装饰器语法糖 @
@timer
def work():
for i in range(100000):
pass
# 调用,和原来调用方式一模一样
work()
等价于:
work = timer(work)2. 原函数带参数怎么办?
用
*args, **kwargs 万能传参适配所有函数:python
运行
def timer(func):
def wrapper(*args, **kwargs):
import time
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print(f"耗时:{end - start:.4f}")
return res # 把原函数返回值返回出去
return wrapper
@timer
def add(a, b):
return a + b
print(add(1, 2))
三、保留原函数信息:functools.wraps
不加
wraps 时,add.__name__ 会变成 wrapper,调试会混乱。python
运行
from functools import wraps
def timer(func):
@wraps(func) # 把原函数的属性复制给包装函数
def wrapper(*args, **kwargs):
import time
start = time.time()
res = func(*args, **kwargs)
print(time.time() - start)
return res
return wrapper
@timer
def add(a, b):
return a + b
print(add.__name__) # 输出 add,而不是 wrapper
四、带参数的装饰器
需要给装饰器传配置参数时,多套一层函数:
python
运行
from functools import wraps
def logger(level):
# 外层:接收装饰器参数
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"[{level}] 正在执行函数:{func.__name__}")
return func(*args, **kwargs)
return decorator
return decorator
# 调用装饰器时传参
@logger(level="INFO")
def login():
print("用户登录成功")
login()
执行流程:
login = logger("INFO")(login)五、多个装饰器叠加
python
运行
def decor1(func):
def wrapper():
print("装饰器1开始")
func()
print("装饰器1结束")
return wrapper
def decor2(func):
def wrapper():
print("装饰器2开始")
func()
print("装饰器2结束")
return wrapper
@decor1
@decor2
def test():
print("原函数执行")
test()
执行顺序:
从上到下装饰,从下到上执行
plaintext
装饰器1开始
装饰器2开始
原函数执行
装饰器2结束
装饰器1结束
六、类装饰器(用类实现装饰)
依靠
__call__ 方法,实例可调用:python
运行
class Timer:
def __call__(self, func):
from functools import wraps
@wraps(func)
def wrapper(*args, **kwargs):
import time
s = time.time()
ret = func(*args, **kwargs)
print(time.time() - s)
return ret
return wrapper
@Timer()
def demo():
sum(range(100000))
demo()
七、常用实战场景示例
1. 权限校验装饰器
python
运行
from functools import wraps
def check_permission(role):
def decorator(func):
@wraps(func)
def wrapper(user, *args, **kwargs):
if user != role:
raise PermissionError("权限不足")
return func(user, *args, **kwargs)
return decorator
return decorator
@check_permission("admin")
def delete_data(user):
print("删除数据成功")
# delete_data("guest") # 抛异常
delete_data("admin")
2. 缓存装饰器(简易 lru 缓存)
python
运行
def cache(func):
data = {}
@wraps(func)
def wrapper(*args):
if args not in data:
data[args] = func(*args)
return data[args]
return wrapper
@cache
def fib(n):
if n <= 2:
return 1
return fib(n-1) + fib(n-2)
print(fib(50))
八、面试高频考点总结
- 装饰器本质:高阶函数 + 闭包
- 为什么用
functools.wraps:保留原函数名称、文档注释等元信息 - 带参装饰器需要三层函数,无参两层
- 多个装饰器:由下往上执行,由上往下装饰
- 类装饰器靠
__call__实现可调用
浙公网安备 33010602011771号