ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

CPython 互操作性更新:OrderedDict.update 现在可直接接受 frozendict 参数

CPython 互操作性更新:OrderedDict.update 现在可直接接受 frozendict 参数 CPython 互操作性更新OrderedDict.update 现在可直接接受 frozendict 参数【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython本篇指南围绕 CPython 仓库中的一条新增特性展开collections.OrderedDict.update方法现在可以接受frozendict冻结字典作为参数。文章将说明该改动在 Lib/collections/init.py 与 Objects/dictobject.c 中的实现机理并给出可复制的代码示例与测试验证方法帮助读者理解可变有序字典与不可变映射类型之间的互操作能力。1. 背景一条 NEWS 条目背后的新能力在 CPython 仓库的 Misc/NEWS.d/next/Core_and_Builtins/2026-06-28-14-31-03.gh-issue-152492._09Zee.rst 中记录了本次变更collections.OrderedDictupdatemethod can now acceptfrozendictas an argument.也就是说OrderedDict.update(frozendict_instance)这种调用方式此前不被支持而现在可以正常工作了。该改动关联 issue gh-152492属于 CPython 的 Core and Builtins 范畴。在深入了解改动细节之前需要先厘清两个主角类型OrderedDict位于 Lib/collections/init.py 的class OrderedDict(dict)是一个记住插入顺序的字典内部通过一条循环双向链表维护键的顺序源码注释中详细描述了self.__map映射与哨兵节点self.__root的结构。frozendictCPython 3.14 起新增的不可变映射类型其核心实现位于 Objects/dictobject.c 的// --- frozendict implementation ---段落。它继承dict的全部读取能力keys、items、values、get、__contains__、__getitem__、__len__等但任何赋值、删除操作都会抛出TypeError报错信息形如frozendict object does not support assignment见 Objects/dictobject.c 的frozendict_does_not_support宏。本次改动的核心价值在于打通了可变有序字典与不可变映射之间的数据搬运通道把frozendict作为update的源将其中全部键值对按迭代顺序合并进OrderedDict。2. 为什么曾经不行从update的底层实现看问题根源OrderedDict继承自dict但它的update并不直接使用 C 层实现而是在 Lib/collections/init.py 中显式绑定为update __update _collections_abc.MutableMapping.update即复用 Lib/_collections_abc.py 中MutableMapping.update的纯 Python 实现。该实现的核心逻辑如下def update(self, other(), /, **kwds): if isinstance(other, Mapping): for key in other: self[key] other[key] elif hasattr(other, keys): for key in other.keys(): self[key] other[key] else: for key, value in other: self[key] value for key, value in kwds.items(): self[key] value这条路本身对实现了 Mapping 协议的对象是通用的理论上frozendict应该可以走isinstance(other, Mapping)分支。问题出在更底层的 C 实现上在 CPython 中dict的更新走的是 Objects/dictobject.c 的dict_merge/PyDict_Merge系列函数而这些函数对参数类型有严格的检查。看 Objects/dictobject.c 的dict_merge它首先判断b是否是真正的字典类型PyAnyDict_Check(b) Py_TYPE(b)-tp_iter dict_iter若是则走快速路径dict_dict_merge否则走慢速通用路径通过PyMapping_Keys(b)获取键列表再逐个读取。慢速路径本身能处理任何实现映射协议的对象。真正的问题是另一侧——CPython 3.14 起普通dict的所有修改型操作update、|、下标赋值等都必须先通过can_modify_dict(mp)检查而frozendict类型的对象在赋值、删除时会触发frozendict_does_not_support(...)抛错见 Objects/dictobject.c、Objects/dictobject.c 等处。于是在OrderedDict.update(frozendict(...))这条调用链上若底层误把frozendict当作待修改的目标字典来处理就会因为目标不可变而直接失败。这正是本次 NEWS 条目所要修复的互操作断点。3. 新行为的验证直接运行即可复现在当前的 CPython 源码树中构建出解释器后下面这段代码即可验证新特性from collections import OrderedDict fd frozendict({a: 1, b: 2, c: 3}) od OrderedDict() od.update(fd) # 新行为接受 frozendict 作为 update 参数 print(od) # OrderedDict([(a, 1), (b, 2), (c, 3)]) # 在已有内容上继续合并原顺序保持新键追加到末尾 od.update(frozendict({d: 4})) print(list(od.items())) # [(a, 1), (b, 2), (c, 3), (d, 4)] # 与关键字参数、其他映射混合使用 od2 OrderedDict() od2.update(frozendict({x: 10}), y20) print(od2) # OrderedDict([(x, 10), (y, 20)])注意frozendict目前仍属于新引入的类型在dict之外与OrderedDict组合使用时需以当前分支的构建版本为准。若只想做快速冒烟测试可以直接运行仓库自带测试cd /data/web/disk1/git_repo/GitHub_Trending/cp/cpython ./python -m test test_ordered_dict -v其中 Lib/test/test_ordered_dict.py 的test_update用例已针对 frozendict 输入补充了断言od OrderedDict() od.update(frozendict(pairs)) self.assertEqual(sorted(od.items()), pairs) # frozendict input此外同文件中还有frozendict与OrderedDict互相转换的断言Lib/test/test_ordered_dict.pyself.assertEqual(od1, frozendict(od2)) self.assertEqual(frozendict(od1), od2)4. 其他 frozendict 的update互操作dict 与 Counter值得说明的是本次互操作改进并不局限于OrderedDict。从测试代码看普通dict与Counter也能以frozendict为源进行更新普通dictd.update(frozendict(...))走 C 层dict_update_common→dict_update_arg→dict_mergeObjects/dictobject.cfrozendict作为另一个映射被逐个键读取并写入目标。CounterCounter.update是计数累加语义见 Lib/collections/init.py当传入对象isinstance(iterable, _collections_abc.Mapping)时逐项累加计数Lib/collections/init.py。from collections import Counter, OrderedDict d {} d.update(frozendict({a: 1})) print(d) # {a: 1} c Counter() c.update(frozendict({a: 2, b: 3})) print(c) # Counter({b: 3, a: 2})不过要强调的是frozendict本身是不可变的任何把frozendict当作被更新目标的尝试仍然会失败fd frozendict({a: 1}) fd.update({b: 2}) # TypeError: frozendict object does not support assignment这是设计使然——frozendict的frozendict_does_not_support错误路径在 Objects/dictobject.c 之后被全面铺开确保所有赋值类操作都明确报错而不是静默失败。5. 底层原理dict_merge 的映射源路径要真正理解为什么这条 NEWS 成立可以顺着 C 层调用链走一遍。以OrderedDict().update(fd)为例fd为 frozendictOrderedDict.update在 Lib/collections/init.py 绑定为MutableMapping.update纯 Python 实现。MutableMapping.update判断isinstance(other, Mapping)随后对每个 key 执行self[key] other[key]Lib/_collections_abc.py等价于逐项调用OrderedDict.__setitem__。若底层曾试图通过 C 层PyDict_Update/PyDict_MergeObjects/dictobject.c处理则进入dict_merge_apiObjects/dictobject.c只有目标a必须是可修改的dict而源b既可以是具体字典快速路径dict_dict_merge也可以是任何支持keys()与__getitem__的抽象映射慢速路径Objects/dictobject.c。frozendict作为源时满足慢速路径的全部前提它实现了完整的映射读取协议因而合并成功同时由于目标OrderedDict是可变对象can_modify_dict检查通过不会触发frozendict_does_not_support的报错路径。从 Objects/dictobject.c 的PyFrozenDict_Type定义可见frozendict的映射槽位frozendict_as_mapping只包含mp_length与mp_subscript即只承诺可读、可测长度这从类型层面保证了它是纯只读映射可作为任何update的合法数据源。6. 小结本次变更让OrderedDict.update可以接受frozendict作为参数使不可变映射能作为数据源批量合并进有序字典相关断言已写入 Lib/test/test_ordered_dict.py。变更记录位于 Misc/NEWS.d/next/Core_and_Builtins/2026-06-28-14-31-03.gh-issue-152492._09Zee.rst实现基础是 Objects/dictobject.c 中dict_merge对抽象映射源的通用支持。使用建议需要把frozendict转成有序可变字典时OrderedDict()update(frozendict_obj)是最直接的方式需要进一步了解frozendict类型本身哈希、|合并、pickle 支持等可继续阅读 Objects/dictobject.c 的 frozendict 实现段落以及 Lib/test/test_dict.py 起的FrozenDictTests测试类。【免费下载链接】cpythonThe Python programming language项目地址: https://gitcode.com/GitHub_Trending/cp/cpython创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进