当前位置:首页>python>一文读懂Python装饰器|原理、语法、实战案例

一文读懂Python装饰器|原理、语法、实战案例

  • 2026-09-29 04:13:59
一文读懂Python装饰器|原理、语法、实战案例
很多初学者看到代码里 @xxx 就一头雾水,这个@语法对应的就是装饰器(Decorator)。
装饰器可以在不修改原函数源代码、不改变调用方式的前提下,动态给函数增加额外功能。
一、底层原理铺垫
Python中函数是一等公民:
函数可以赋值给变量
函数可以作为参数传入另一个函数
函数可以作为返回值返回
装饰器本质 = 高阶函数 + 闭包 + @语法糖
通俗比喻
原函数 = 一件T恤
装饰器 = 外套。穿上外套(使用装饰器),T恤本身没有改动,但是新增了保暖功能。调用T恤,依然是原来的调用方式。
二、最简手写装饰器(不使用@语法糖)
先看懂写法,再理解@。
def decorator(func):    # 内层wrapper:包装原函数    def wrapper(*args, **kwargs):        print("【执行前】扩展逻辑")        # 执行原始函数        res = func(*args, **kwargs)        print("【执行后】扩展逻辑")        return res    return wrapperdef hello(name):    print(f"Hello {name}")    return "完成"# 手动包装hello = decorator(hello)result = hello("小明")print(result)
运行结果:
【执行前】扩展逻辑
Hello 小明
【执行后】扩展逻辑
完成
hello = decorator(hello) 就是核心:把原始函数传入装饰器,接收包装后的新函数。
三、@语法糖(日常标准写法)
@装饰器名 等价于上面 func = decorator(func),简化代码。
def decorator(func):    def wrapper(*args, **kwargs):        print("执行前")        res = func(*args, **kwargs)        print("执行后")        return res    return wrapper# 语法糖@decoratordef hello(name):    print(f"Hello {name}")hello("小明")
⚠️关键点:*args, **kwargs 保证装饰器兼容任意参数的函数。
四、重大坑点:函数元信息丢失
执行下面代码观察问题:
def decorator(func):    def wrapper(*args, **kwargs):        return func(*args, **kwargs)    return wrapper@decoratordef add(a,b):    """计算两数之和"""    return a + bprint(add.__name__)print(add.__doc__)
输出:
wrapper
None
装饰后,函数名、文档注释全部变成wrapper的信息,调试、生成文档都会出问题。
✅ 解决方案:使用functools.wraps(工业开发强制规范)
from functools import wrapsdef decorator(func):    @wraps(func)  # 复制原函数元信息    def wrapper(*args, **kwargs):        return func(*args, **kwargs)    return wrapper@decoratordef add(a,b):    """计算两数之和"""    return a + bprint(add.__name__)    # addprint(add.__doc__)     # 计算两数之和
五、进阶1:带参数的装饰器
需求:装饰器本身需要传入配置参数。
结构:三层函数嵌套
from functools import wraps# 第一层:接收装饰器参数def logger(level="INFO"):    # 第二层:接收原始函数    def decorator(func):        @wraps(func)        # 第三层:包装逻辑        def wrapper(*args, **kwargs):            print(f"[{level}] 调用函数:{func.__name__}")            res = func(*args, **kwargs)            print(f"[{level}] {func.__name__}执行结束")            return res        return wrapper    return decorator# 使用方式,需要加括号传参@logger(level="DEBUG")def calc(x):    return x * 2calc(10)
等价写法:calc = logger("DEBUG")(calc)
六、进阶2:多个装饰器叠加
def deco1(func):    @wraps(func)    def wrapper(*args, **kwargs):        print("deco1 before")        res = func(*args, **kwargs)        print("deco1 after")        return res    return wrapperdef deco2(func):    @wraps(func)    def wrapper(*args, **kwargs):        print("deco2 before")        res = func(*args, **kwargs)        print("deco2 after")        return res    return wrapper@deco1@deco2def test():    print("原始函数执行")test()
执行顺序重点
deco1 before
deco2 before
原始函数执行
deco2 after
deco1 after
装饰顺序:自上而下;执行顺序:自下而上。
等价代码:test = deco1(deco2(test))
七、工程实战常用装饰器案例
案例1:计时装饰器(定位慢函数)
import timefrom functools import wrapsdef timer(func):    @wraps(func)    def wrapper(*args, **kwargs):        start = time.perf_counter()        result = func(*args, **kwargs)        cost = time.perf_counter() - start        print(f"函数 {func.__name__} 耗时:{cost:.4f}s")        return result    return wrapper@timerdef slow_func():    time.sleep(0.5)slow_func()
案例2:异常重试装饰器
from functools import wrapsdef retry(max_times=3):    def decorator(func):        @wraps(func)        def wrapper(*args, **kwargs):            times = 0            while times < max_times:                try:                    return func(*args, **kwargs)                except Exception as e:                    times += 1                    print(f"执行失败,重试{times}/{max_times}, err:{e}")            raise Exception("多次重试仍然失败")        return wrapper    return decorator@retry(max_times=2)def connect_db():    raise ConnectionError("数据库连接失败")
案例3:权限校验(Web接口常用)
from functools import wrapsdef login_required(func):    @wraps(func)    def wrapper(user, *args, **kwargs):        if not user.get("is_login"):            return {"code":401, "msg":"请登录"}        return func(user, *args, **kwargs)    return wrapper@login_requireddef get_user_info(user):    return {"code":200, "data":user}print(get_user_info({"is_login":False}))
八、Python内置装饰器(日常高频)
@property:把方法变成属性@staticmethod 静态方法@classmethod 类方法functools.lru_cache 缓存装饰器,递归优化神器
九、装饰器适用场景总结
✅ 适合统一横向通用逻辑:
函数执行耗时统计
日志统一记录(入参、返回值)
接口鉴权、参数校验
请求失败自动重试
结果缓存、限流
异常统一捕获
❌ 不适合:业务核心逻辑,不要把复杂业务全部塞进装饰器。

最新文章

随机文章