直接遍历时用 del 会触发 RuntimeError,因字典动态哈希表特性导致迭代器失效;应先收集待删键再统一删除,或用字典推导式重建。

直接在遍历时用 del 会触发 RuntimeError
Python 字典是动态哈希表,遍历过程中修改其大小(比如删键)会导致内部迭代器失效。一旦执行类似 for k in d: del d[k],立刻抛出 RuntimeError: dictionary changed size during iteration。这不是偶然 bug,而是明确的运行时保护机制。
常见误操作包括:在 for k, v in d.items() 循环里调用 del d[k],或用 pop(k) 改变字典结构的同时继续迭代。
推荐做法:先收集要删的键,再统一删除
最安全、最易读的方式是分两步:先遍历获取待删除键的列表,再对这些键执行删除。这样遍历和修改完全分离,无任何冲突。
- 适用于所有 Python 版本,语义清晰,调试友好
- 如果键数量少,性能影响可忽略;即使上万键,内存开销也远小于其他方案
- 注意不要用
list(d.keys())后再循环删——虽然能跑通,但它是冗余拷贝,不如直接用list(d)(字典本身可直接转为键列表)
示例:
立即学习“Python免费学习笔记(深入)”;
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
keys_to_remove = [k for k in d if k in ['b', 'd']] # 条件判断
for k in keys_to_remove:
del d[k]
# d 变为 {'a': 1, 'c': 3}
用字典推导式替代「遍历+删」更简洁
如果你本质是要保留满足某条件的键值对,而不是「删不满足的」,那直接重建字典比删旧字典更自然、更函数式。
- 推导式生成新字典,原字典不变,线程安全(无副作用)
- 避免中间列表分配,内存更省;CPython 下通常也更快
- 不适用于需要在删除前对每个被删项做副作用操作(如日志、释放资源)的场景
示例:
立即学习“Python免费学习笔记(深入)”;
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4}
d = {k: v for k, v in d.items() if k not in ['b', 'd']}
# d 变为 {'a': 1, 'c': 3}
极少数场景下可用 popitem(),但别乱用
popitem() 在 Python 3.7+ 是 LIFO(删最后插入的),它本身是原子操作,不会导致迭代错误。但它的行为和「按条件删指定键」完全无关,仅适合清空或实现栈式字典。
- 不能控制删哪个键,无法配合条件逻辑
- 想删全部?用
while d: d.popitem()可行,但远不如d.clear()直观高效 - 误以为
popitem()能安全配合for循环 —— 实际上只要循环体里改了字典大小,依然会崩
真正安全的批量删,只取决于「遍历和修改是否分离」,而不是用了哪个删除函数。










