python 3.9 的 | 和 |= 仅适用于普通 dict,不支持协程对象;必须先 await 获取字典再合并,否则报 typeerror;它本质是同步操作,不触发 i/o 或事件循环。

Python 3.9 的 | 和 |= 对异步字典没直接作用
异步字典本身不是 Python 的内置概念——dict 是同步的,无论你把它放在 async def 函数里还是塞进 asyncio.Queue,它都不会自动变成“异步字典”。Python 3.9 引入的合并运算符 |(字典并集)和 |=(原地更新)只作用于普通 dict 实例,不感知协程、不 await、不调度事件循环。
所以如果你写:
async def merge_dicts():
a = {"x": 1}
b = {"y": 2}
return a | b # ✅ 合法,纯同步操作
这没问题;但如果你期待:
async def merge_awaitables():
a = fetch_config() # 返回 coroutine object
b = load_user_data() # 返回 coroutine object
return a | b # ❌ TypeError: unsupported operand type(s) for |
就会直接报错——因为 | 不知道怎么合并两个协程对象。
想在协程中用 |,得先 await 出字典
合并运算符只认 dict,不认 coroutine。你要做的其实是:先并发或串行获取字典结果,再用 | 合并。关键在于时机:必须在值就绪后才调用 |。
- 串行合并(简单、保序、无竞态):
await一个再await另一个,然后dict1 | dict2 - 并发合并(更快,但需处理异常):用
asyncio.gather(a(), b())得到两个dict,再result1 | result2 - 别对未
await的协程对象用|——它既不会自动 await,也不会抛出“请 await 我”的友好提示,只会报TypeError
示例(并发安全版):
async def get_merged():
d1, d2 = await asyncio.gather(
fetch_api_v1(),
fetch_api_v2()
)
return d1 | d2 # ✅ 此时 d1 和 d2 都是 dict
| 在协程里性能没额外开销,但别误以为它“支持异步”
| 本质是调用 dict.__or__,纯 CPU 操作,毫秒级。它不触发 I/O、不进事件循环、不释放 GIL(因为不涉及系统调用)。所以你在 async 函数里用它,不会阻塞事件循环——但这只是因为它本来就快,不是因为它被“异步化”了。
容易踩的坑:
- 把
await some_dict_like_obj | other_dict当成合法语法(实际会先算|再 await,而左边可能不是 dict) - 在
async for循环里反复用|=累积大字典,导致内存持续增长且无 await 点——表面上“异步”,实则成了同步内存炸弹 - 误以为
typing.Dict | typing.Literal这类类型注解中的|和字典合并有关(那是联合类型,Python 3.10+ 的Union语法糖,完全无关)
真正需要“异步字典”时,得换工具
如果你真需要带异步读写语义的字典(比如键值对存在 Redis 或数据库里),Python 标准库不提供,得靠第三方:
-
aioredis.Redis提供hgetall/hmset等异步哈希操作,但接口不是dict子类,不能直接用| -
async_lru可给异步函数加缓存,但缓存体仍是普通dict,合并逻辑仍需手动await+| - 自己封装?可以,但要注意:重载
__or__返回协程对象会破坏所有现有字典协议(比如dict.update()就不认)
结论很实在:Python 3.9 的 | 是个好用的同步语法糖,但它不延伸异步语义。协程里用它,唯一前提就是——你手里拿的是 dict,不是 coroutine。这点边界模糊了,后面所有 await 和合并逻辑都会错位。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











