Python 3.12 未引入新装饰器语法,@ 语法自2.4版未变;@后不可写 await,因装饰器需静态求值;异步装饰器应返回 async def 包装器,正确处理协程签名与事件循环。

Python 3.12 并没有引入新的装饰器语法——@ 语法本身自 Python 2.4 起就未变,3.12 也未修改其解析规则或语义。所谓“新装饰器语法”是误传,常见于对 PEP 614(放宽装饰器表达式限制)的误解,但它早在 Python 3.9 就已实现,且与异步优化无关。
为什么 @ 后面能写 await func() 是错觉?
你可能在某些代码里看到类似 @await some_async_decorator() 的写法,但这在任何 Python 版本(包括 3.12)中都是语法错误:await 不能出现在装饰器位置,因为装饰器必须在模块加载时静态求值,而 await 是运行时协程操作。
-
async def定义的函数本身不能直接用作装饰器,除非它返回一个可调用对象(如闭包),且该返回值在定义时(非 await 时)就可用 - 试图写
@await my_async_deco()会触发SyntaxError: invalid syntax - 真正可行的是:先
await调用生成装饰器,再手动应用,例如func = await my_async_deco()(func),但这不是装饰器语法,而是显式包装
Python 3.12 中真正影响异步装饰器的改动
3.12 唯一相关变更:asyncio 默认事件循环改为 uvloop 兼容的 AsyncIOEventLoopPolicy(仅限 Linux/macOS),以及 asyncio.run() 内部更严格地检测嵌套调用。这些不影响装饰器写法,但会影响装饰器内部的异步行为表现。
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 若你在装饰器里调用
asyncio.run(),在 3.12 中更可能遇到RuntimeError: asyncio.run() cannot be called from a running event loop - 推荐改用
asyncio.create_task()或显式传递loop参数,而非依赖asyncio.run() -
functools.wraps()对async def函数支持良好,但需确保被包裹函数是协程对象(inspect.iscoroutinefunction返回True),否则await会报TypeError: object XXX can't be used in 'await' expression
实用的异步装饰器写法(兼容 3.12)
真正可靠的方式仍是定义同步装饰器,返回一个 async def 包装器。重点在于正确处理协程签名、上下文和错误传播。
立即学习“Python免费学习笔记(深入)”;
def with_metrics(func):
if not asyncio.iscoroutinefunction(func):
raise TypeError(f"{func.__name__} is not a coroutine function")
<pre class='brush:python;toolbar:false;'>@functools.wraps(func)
async def wrapper(*args, **kwargs):
start = time.time()
try:
result = await func(*args, **kwargs)
print(f"{func.__name__} took {time.time() - start:.3f}s")
return result
except Exception as e:
print(f"{func.__name__} failed: {e}")
raise
return wrapper使用
@with_metrics async def fetch_data(url): return await aiohttp.ClientSession().get(url)
- 必须用
async def wrapper,不能用def wrapper+return await func(...),否则调用者无法await返回值 -
functools.wraps(func)在 3.12 中对协程函数完全有效,但若省略,会导致inspect.signature()获取不到原函数参数 - 避免在装饰器内启动新事件循环(如
asyncio.new_event_loop()),3.12 对多循环交互更敏感,容易触发RuntimeError: This event loop is already running
真正的复杂点不在语法,而在事件循环生命周期管理——装饰器执行时机早于请求上下文,而异步资源(如数据库连接池、HTTP session)往往需要按请求生命周期创建。硬编码在装饰器里的 aiohttp.ClientSession() 实例极易引发连接泄漏或并发错误。

















