python字典迭代器通过版本号校验防并发修改,循环中增删键会触发runtimeerror;安全做法包括list(d.keys())、d.copy().items()和字典推导式,但需注意浅拷贝限制及隐式修改风险。

字典迭代器底层有版本号校验
Python 的 dict 迭代器不是“拍快照”,而是持有一个指向原字典的引用,并记录当前哈希表的元素数量(ma_used)和自己的迭代版本(di_used)。每次调用 next() 前,解释器会比对这两个值;一旦你在循环中执行 del d[k] 或 d[k] = v,ma_used 就会变,校验失败,立刻抛出 RuntimeError: dictionary changed size during iteration。
list(d.keys()) 是最常用的安全解法
它把当前所有键一次性转成独立列表,后续对原字典的增删不影响这个列表:
-
for k in list(d.keys()):—— 适用于按键名条件删除、重命名、批量清理 -
for k, v in list(d.items()):—— 适合需要同时读键和值再决定是否删键的场景 - 注意:
list(d)等价于list(d.keys()),但语义模糊,建议显式写后者
d.copy().items() 适合值较重的场景
当字典值是大型对象(如嵌套结构、大字符串、IO 缓冲区),重复查 d[k] 开销大时,用副本遍历能避免多次访问:
-
for k, v in d.copy().items():——v是原字典值的引用,只读安全 - 别写
d.items().copy():dict_items对象没有copy()方法 - 如果值本身是可变对象(比如
list),修改v仍会影响原字典 —— 但这不是迭代问题,是浅拷贝固有限制
字典推导式不能替代所有逻辑
它本质是构造新字典,原字典不动,所以绝对安全:
-
d = {k: v for k, v in d.items() if v is not None}—— 过滤场景首选 - 但它无法处理“先改某个值,再根据新值决定是否删键”这类混合操作
- 推导式里调用
pop()或其他副作用函数会导致行为不可预测,应避免
cleanup(d)),或者在 globals() 这类动态字典上遍历时触发递归引用,也会中招。安全边界不在“你写了什么”,而在“字典结构是否在迭代期间被改变”。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











