You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python如何叠加使用多个装饰器?多装饰器执行行为解析

Python多装饰器叠加执行规则解答

双装饰器直接组合的有效性

你写的backoff.on_exception+@timeit的组合写法语法完全合法,可以正常运行,但必须先明确Python装饰器的核心执行规则:装饰器是从距离被装饰函数最近的位置开始,由下往上逐层包装的,你写的这段代码:

@backoff.on_exception(backoff.expo,
                      (requests.exceptions.Timeout,
                       requests.exceptions.ConnectionError))
@timeit
def get_url(url):
    return requests.get(url)

等价于手动执行包装逻辑:

def get_url(url):
    return requests.get(url)
get_url = timeit(get_url)
get_url = backoff.on_exception(backoff.expo, (requests.exceptions.Timeout, requests.exceptions.ConnectionError))(get_url)

@timeit装饰器的统计范围

这也是核心疑问:按上面的写法,@timeit紧贴原始请求函数,它只包装最原生的requests.get单次调用逻辑:

  • 每次触发重试时,backoff逻辑会重新调用被timeit包装的单次请求函数,因此每一次单独的请求耗时都会被timeit单独统计,重试之间的指数退避等待时间不会被计入统计。
  • 举个实际场景:第一次请求超时耗时1s,退避等待2s后第二次请求耗时0.5s成功,timeit会先后输出两条统计结果,分别对应1s、0.5s的两次请求耗时,不会统计包含等待时间在内的3.5s总时长。
  • 如果你需要统计包含所有重试、退避等待的完整流程总耗时,只需要把@timeit放到所有backoff装饰器的最外层(最顶部)即可,此时timeit包装的是整个带重试逻辑的完整流程,只会在全流程结束后输出一次总耗时。

多层backoff装饰器叠加的运行行为

你写的三层装饰器代码:

@backoff.on_exception(backoff.expo,
                      SomeOtherError)
@backoff.on_exception(backoff.expo,
                      (requests.exceptions.Timeout,
                       requests.exceptions.ConnectionError))
@timeit
def get_url(url):
    return requests.get(url)

执行逻辑是分层嵌套的,从内到外的行为如下:

  • 最内层是网络错误专属的退避重试逻辑:只要单次请求抛出Timeout/ConnectionError,这一层就会独立触发指数退避重试,直到达到该层配置的最大重试次数后,才会把未处理的异常向外层抛出。
  • 外层是SomeOtherError专属的退避重试逻辑:它只会接收到内层没处理完抛上来的异常,只有当异常类型匹配SomeOtherError时,才会触发自己的指数退避重试,重新调用内层的整套逻辑(也就是重新走内层的网络错误重试流程)。
  • 只要执行到最原始的请求调用,@timeit就会统计这一次单次请求的耗时,退避等待时间始终不会被计入timeit的统计结果。

内容的提问来源于stack exchange,提问作者Exploring

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 23:48:21