Python装饰器三层嵌套实战:手写一个带过期时间的缓存装饰器

举报
yd_255726501 发表于 2026/10/30 09:20:33 2026/10/30
【摘要】 在 Python 开发中,装饰器几乎无处不在:Web 框架的路由注册、测试框架的用例标记、functools 的缓存都离不开它。很多人能熟练使用 @lru_cache,却说不清"带参数的装饰器"为什么需要三层函数嵌套。今天我们亲手写一个带过期时间(TTL)的缓存装饰器,把这个知识点彻底讲透。 一、装饰器的本质:函数也是一种对象Python 里函数是一等公民:可以作为参数传递、作为返回值返回、...

在 Python 开发中,装饰器几乎无处不在:Web 框架的路由注册、测试框架的用例标记、functools 的缓存都离不开它。很多人能熟练使用 @lru_cache,却说不清"带参数的装饰器"为什么需要三层函数嵌套。今天我们亲手写一个带过期时间(TTL)的缓存装饰器,把这个知识点彻底讲透。

一、装饰器的本质:函数也是一种对象

Python 里函数是一等公民:可以作为参数传递、作为返回值返回、可以被重新赋值。装饰器的本质,就是"接收一个函数,返回一个增强后的新函数"的语法糖:

@timer
def slow():
    ...
# 完全等价于:slow = timer(slow)

无参装饰器只需要两层嵌套:外层接收被装饰的函数,内层 wrapper 负责增强逻辑。

import time
import functools

def timer(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        print(f"{func.__name__} 耗时 {time.time() - start:.3f}s")
        return result
    return wrapper

二、带参数时为什么需要三层嵌套?

当我们想写 @cache(ttl=60) 这种带参数的装饰器时,执行顺序变成了两步:先调用 cache(ttl=60) 得到真正的装饰器,再用它去装饰函数。于是必须再包一层:

def cache(ttl=60):
    def decorator(func):
        store = {}

        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            key = (args, tuple(sorted(kwargs.items())))
            now = time.time()
            if key in store:
                value, expire = store[key]
                if now < expire:
                    return value  # 缓存命中,直接返回
            value = func(*args, **kwargs)
            store[key] = (value, now + ttl)
            return value

        return wrapper
    return decorator

三层各司其职:最外层接收装饰器参数,中间层接收被装饰的函数,最内层执行真正的增强逻辑。记住"想传几次参数就嵌套几层",就再也不会写错。

三、几个容易踩的坑

  1. 忘记 functools.wraps:不加它,被装饰函数的 __name__ 会变成 wrapper,日志和文档工具就认不出原函数了。
  2. 可变参数别漏了:wrapper 一定要用 *args, **kwargs 透传,否则装饰带参数的函数会直接报错。
  3. 缓存键要可哈希:上面用 args 加排序后的 kwargs 元组做键;如果参数里有字典、列表,需要先转成可哈希形式。
  4. 线程安全:多线程场景下读写 store 建议加锁;生产环境可以直接用 cachetools.TTLCache,它已经处理好了这些细节。

四、小结

装饰器写几层,取决于"你在调用装饰器时想传几次参数":无参两层、带参三层。理解了这个规律,权限校验、重试、限流这类装饰器都能举一反三。建议把上面的 TTL 缓存亲手敲一遍并跑通,装饰器这个知识点就真正属于你了。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。