装饰器
装饰器:
点击查看代码
def logger(fn):
def wrapper(*args,**kwargs): #此处的*和**是形参类型
print("before, 此处可以添加函数功能")
ret=fn(*args,**kwargs) #此处的*和**是参数解构
print("after,此处也可以添加函数功能")
return ret #将fn函数的返回值返回
return wrapper #将内部函数引用返回
@logger # add1=logger(add1) #语法糖,左边的add1和右边的add1不是一个概念了,右边的add1就是logger()函数里形参fn,左边则是logger函数里的内部函数wrapper的引用
#左边也可以是其他名称,只是为了调用代码不变,让调用者感觉不到函数被改造了所以左边仍然用add1
def add1(x,y):
return x+y
add1(8,10)
python文档
python是文档字符串Documentation Strings
在函数语句块的第1行,且习惯是多行的文本,所以多使用三引号
惯例是首字母大写,第1行写概述,空一行,第3行写详细描述。
可以使用特殊属性__doc__ 访问这个文档
带参装饰器就是可以接收参数的装饰器,它比普通装饰器多了一层函数嵌套,核心作用是:让装饰器变得可配置、更灵活。
先快速对比:
普通装饰器:@decorator
带参装饰器:@decorator(参数1, 参数2...)
一、带参装饰器的标准结构(必记)
带参装饰器一共 3 层函数:
最外层:接收装饰器的参数(你传进来的配置)
中间层:接收被装饰的函数(真正的装饰器逻辑)
最内层:包装函数,执行原函数前后的增强逻辑
点击查看代码
# 标准三层结构
def 外层(装饰器参数): # 接收装饰器参数
def 中间层(被装饰函数): # 接收被装饰的函数
def 内层(*args, **kwargs): # 接收原函数的参数
# 原函数执行前逻辑
结果 = 被装饰函数(*args, **kwargs)
# 原函数执行后逻辑
return 结果
return 内层
return 中间层
点击查看代码
# 三层带参装饰器
def log(level):
# 第一层:接收装饰器参数(level)
def decorator(func):
# 第二层:接收被装饰的函数
def wrapper(*args, **kwargs):
# 第三层:执行原函数+增强逻辑
print(f"[{level}] 开始执行函数:{func.__name__}")
result = func(*args, **kwargs) # 执行原函数
print(f"[{level}] 函数执行完成\n")
return result
return wrapper
return decorator
# 使用带参装饰器
@log("INFO") # 传入参数:INFO级别日志
def add(a, b):
return a + b
@log("ERROR") # 传入参数:ERROR级别日志
def divide(a, b):
return a / b
# 测试
add(1, 2)
divide(6, 2)
[ERROR] 开始执行函数:divide
[ERROR] 函数执行完成
执行流程(看懂就彻底理解)
@log("INFO") 先执行 log("INFO"),返回 decorator 函数
@decorator 装饰 add 函数,把 add 传给 decorator
调用 add() 时,实际执行 wrapper()
三、进阶案例:带多个参数的装饰器(控制执行次数)
需求:装饰器接收次数 + 前缀两个参数,让函数重复执行 N 次。
点击查看代码
def repeat(times, prefix):
# 第一层:接收 次数、前缀 两个参数
def decorator(func):
def wrapper(*args, **kwargs):
print(f"{prefix} 准备重复执行 {times} 次")
for i in range(times):
print(f"第 {i+1} 次执行:")
func(*args, **kwargs)
print("-" * 20)
return wrapper
return decorator
# 使用:重复3次,前缀=【任务】
@repeat(3, "【任务】")
def say_hello(name):
print(f"Hello {name}")
# 测试
say_hello("小明")
四、实战常用:带参装饰器验证权限
非常实用的场景:给接口 / 函数做权限校验,装饰器参数指定允许的角色。
点击查看代码
# 权限验证带参装饰器
def permission(allow_role):
def decorator(func):
def wrapper(user_role, *args, **kwargs):
# 核心逻辑:判断用户角色是否允许
if user_role == allow_role:
return func(user_role, *args, **kwargs)
else:
return f"权限不足!允许角色:{allow_role},你的角色:{user_role}"
return wrapper
return decorator
# 只有 admin 能调用
@permission("admin")
def delete_data(user_role):
return "删除数据成功"
# 只有 user 能调用
@permission("user")
def view_data(user_role):
return "查看数据成功"
# 测试
print(delete_data("admin")) # 有权限
print(delete_data("user")) # 无权限
print(view_data("user")) # 有权限
五、保留原函数信息(重要!)
默认装饰器会覆盖原函数的 name、doc,需要用 functools.wraps 修复。
标准优雅写法(工作中必须用)
点击查看代码
from functools import wraps
def log(level):
def decorator(func):
@wraps(func) # 保留原函数信息
def wrapper(*args, **kwargs):
print(f"[{level}] 调用 {func.__name__}")
return func(*args, **kwargs)
return wrapper
return decorator
@log("DEBUG")
def test():
"""这是测试函数"""
pass
print(test.__name__) # 输出 test(没有 wraps 会输出 wrapper)
print(test.__doc__) # 输出 这是测试函数
六、带参装饰器执行顺序总结
加载模块时:@log("INFO") → 执行外层函数,返回中间装饰器
装饰阶段:中间装饰器接收被装饰函数,返回包装函数
调用函数时:真正执行包装函数
总结
带参装饰器 = 3 层函数:参数层 → 装饰器层 → 包装层
用途:让装饰器可配置(日志级别、权限、执行次数等)
标准格式:一定要配合 @wraps(func) 使用
调用方式:@装饰器(参数)
注意 python的装饰器是自下而上执行,内层装饰器限制性,外层装饰器后执行。当有多个装饰器装饰一个函数时一定要搞清楚,确定好装饰器顺序,不能随便写
functools模块
functools.update_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)
类似copy_properties功能
wrapper包装函数,wrapped被包装函数
元组WRAPPER_ASSIGNMENT中,是要被覆盖的属性 'module','name','qualname','doc','annotations'模块名、名称、限定名、文档、参数注释
元组WRAPPER_UPDATES中是要被更新的属性,__dict__属性字典
增加一个__wrapped__属性,保留wrapped函数
partial方法:
1、偏函数,把函数部分参数固定下来,相当于为部分参数添加了一个固定的默认值,形成一个新的函数并返回
2、从partial生成的新函数,是对原函数的封装
eg:
点击查看代码
import functoools
import inspect
def add(x,y):
return x+y
new_add=functools.partial(add,y=5)
print(new_add(7))
print(new_add(7,y=6))
print(new_add(y=10,x=6))
print(inspect.signature(new_add))
使用前提:
同样的函数参数一定得到同样的结果
函数执行的时间很长,切要多次执行
本质:函数调用的参数-->返回值
缺点:
不支持缓存过期,key无法过期失效
不支持清除工作
不支持分布式,是一个单机的缓存
适用场景:单机上需要空间换时间的地方,可以用缓存来将计算变成快速地查询
本文来自博客园,作者:{睡佛爷},转载请注明原文链接:{https://www.cnblogs.com/sfy-cnblogs.com}
浙公网安备 33010602011771号