
很多人觉得装饰器很简单——加个 @ 符号嘛,看几眼就能上手。
这没错。但问题在于,能看懂装饰器和能写装饰器,中间隔着一道真正的技术鸿沟。
我自己写 Python 快十年了,带过不少团队新人。见过太多人在「使用装饰器」这一步如鱼得水,却始终跨不过「编写装饰器」的门槛。他们能熟练地用 Flask 的 @app.route、用 @property、用各种第三方库的装饰器,却从没亲手写过一行自己的。
直到有一天,他们遇到了必须自己解决的问题——日志要怎么统一记录?API 超时要不要重试?参数校验能不能一次搞定?
这时候才发现,不会写装饰器的人,要么到处复制粘贴,要么硬编码一堆重复逻辑。

往期阅读>>>
Python 20 个文本分析的库:效率提升 10 倍的秘密武器
Python 自动化管理Jenkins的15个实用脚本,提升效率
App2Docker:如何无需编写Dockerfile也可以创建容器镜像
Python 自动化识别Nginx配置并导出为excel文件,提升Nginx管理效率
先说个真实的场景。
你接手了一个订单系统,需要在每个关键函数调用时记录日志。最土的办法就是在每个函数开头加一句 logger.info("调用了 xxx 函数")。结果呢?一个系统下来,三四十个地方都要加,改动量巨大,而且以后维护起来简直是噩梦。
懂装饰器的人怎么解决这个问题?
def log_order_event(func): def wrapper(*args, **kwargs): logger.info("调用函数: %s", func.__name__) result = func(*args, **kwargs) logger.debug("返回结果: %s", result) return result return wrapper@log_order_eventdef order_pizza(toppings): # 业务逻辑 pass一行 @log_order_event,搞定所有日志需求。代码干净、可读性高,后续维护只需要改装饰器本身。
想象一个场景:你的接口要求每次返回的数据里,summary 字段不能超过 80 个字符。正常做法是在每个函数里写 if 判断。但如果你有十几个函数都要这个校验呢?
def validate_summary(func): def wrapper(*args, **kwargs): data = func(*args, **kwargs) if len(data["summary"]) > 80: raise ValueError("Summary too long") return data return wrapper这个装饰器写好了,以后只要加个 @validate_summary,所有函数的校验逻辑瞬间生效。
写过大型项目的工程师都知道,这种横切关注点的需求有多普遍。 而装饰器,恰恰是处理这类问题最优雅的方式。
调用第三方 API,网络抖动导致偶发 500 错误,要不要重试?
正常人会这样写:
resp = Nonewhile True: resp = make_api_call() if resp.status_code == 500 and tries < MAX_TRIES: tries += 1 continue break然后在项目里复制粘贴这段代码二三十遍。
懂装饰器的人会写一个 @retry,然后——没有了,就一行的事。
@retrydef make_api_call(): # 业务逻辑 pass
说个很多人不知道的冷知识。
Flask 之所以能用 @app.route 绑定路由,背后的原理就是装饰器——route 方法返回的,正是一个装饰器函数。
@app.route("/tasks/", methods=["GET"])def get_all_tasks(): return json.dumps(tasks)还有 Python 内置的 @classmethod、@property,都是装饰器。这些语法糖让 Python 的表达力大大增强,写出来的代码读起来像自然语言。
掌握了装饰器的写法,你就不再只是框架的使用者,而有可能成为框架的设计者
装饰器的核心概念并不复杂:闭包、函数作为一等公民、可变参数。说白了,就是把「行为」抽象成可复用的单元,然后动态地绑定到其他函数上。
如果你现在还用不好装饰器,建议从这三个场景切入:
1. 统一日志记录2. 统一参数校验3. 统一重试机制
挑一个你最痛的需求,动手写一个属于自己的装饰器。写过一遍,胜过看十篇文章。
