sfy-cnblogs

导航

装饰器

装饰器:

点击查看代码
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)
执行结果 [INFO] 开始执行函数:add [INFO] 函数执行完成

[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("小明")
输出: 【任务】 准备重复执行 3 次 第 1 次执行: Hello 小明 第 2 次执行: Hello 小明 第 3 次执行: 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"))      # 有权限
结果: 删除数据成功 权限不足!允许角色:admin,你的角色:user 查看数据成功

五、保留原函数信息(重要!)
默认装饰器会覆盖原函数的 namedoc,需要用 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))
@functools.lru_cache(maxsize=128,typed=False) laest-recently-used 装饰器 lru 最近最少使用 maxsize 如果maxsize设置为None,则禁用LRU功能,并且缓存可以无限制增长。当maxsize是2的幂时,LRU功能执行的最好 typed 如果typed设置为True,则不同类型的函数参数将单独缓存,例如f(3)和f(3.0)将被视为具有不同结果的不同调用 lru_chche装饰器 作用:通过一个字典缓存被装饰函数的调用和返回值,相同入参再次调用时,直接读取缓存,不再重复计算,大幅提升递归、重复调用函数的运行速度。

使用前提:
同样的函数参数一定得到同样的结果
函数执行的时间很长,切要多次执行
本质:函数调用的参数-->返回值
缺点:
不支持缓存过期,key无法过期失效
不支持清除工作
不支持分布式,是一个单机的缓存
适用场景:单机上需要空间换时间的地方,可以用缓存来将计算变成快速地查询

posted on 2026-05-17 13:41  睡佛爷  阅读(3)  评论(0)    收藏  举报