不能边遍历边增删字典键值对,否则触发RuntimeError;根本原因是迭代器绑定哈希表,大小变化导致失效;推荐用list(d.keys())创建键快照遍历。

不能边遍历边删改字典的键或增删键值对,否则必触发 RuntimeError: dictionary changed size during iteration。根本原因不是“语法不允许”,而是 Python 迭代器在底层绑定字典的哈希表结构,大小一变,迭代器就失效。
用 list(d.keys()) 创建键的快照再遍历
这是最常用、最直观的解法。list() 强制把当前所有键转成独立列表,后续对原字典的 del、pop()、update() 都不影响这个列表。
- 适用于:按条件删除键、按值筛选保留键、批量重命名键(先新增后删旧)
- 注意:
list(d)等价于list(d.keys()),但语义不如后者清晰,建议显式写list(d.keys()) - Python 2 中
d.keys()返回列表,所以直接写for k in d.keys():不报错;Python 3 必须加list()包裹
dic = {'a': 1, 'b': 0, 'c': 1, 'd': 0}
for k in list(dic.keys()):
if dic[k] == 0:
del dic[k]
# 结果:{'a': 1, 'c': 1}
用 d.copy().items() 遍历副本并修改原字典
和 list(d.items()) 类似,但更侧重“读取时需同时访问键和值”的场景。d.copy() 返回浅拷贝字典,.items() 基于该副本生成迭代器,安全可靠。
- 优势:避免重复查值(比如
dic[k]),尤其当值是计算密集型对象时 - 风险:如果字典值是可变对象(如嵌套 dict、list),
copy()不深拷贝,修改值本身仍会影响原字典 —— 但本场景只读不写值,无影响 - 别写
d.items().copy():dict_items对象没有copy()方法
d = {"x": [1, 2], "y": None, "z": "ok"}
for k, v in d.copy().items():
if v is None:
del d[k]
用字典推导式替代“遍历 + 删改”逻辑
当目标只是过滤或映射(比如“保留值不为 None 的项”“把所有值乘 2”),直接用推导式最简洁,也最 Pythonic。
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- 本质是构造新字典,原字典完全不动,天然规避迭代冲突
- 无法用于需要“原地修改值 + 同时增删键”的混合操作(比如先改某个值,再根据新值决定是否删掉它)
- 注意:推导式里不能调用会改变原字典状态的函数(如
pop()),否则行为不可预测
# 过滤
new_d = {k: v for k, v in d.items() if v is not None}
<h1>映射(值翻倍)</h1><p>new_d = {k: v * 2 for k, v in d.items() if isinstance(v, (int, float))}</p>
哪些操作其实不会报错?
很多人误以为“只要动了字典就不行”,其实只有改变字典结构(增/删键)才触发错误。以下操作完全安全:
- 修改已有键对应的值:
d["a"] = 999或d.update({"a": 999}) - 对值做原地操作(前提是值本身可变):
d["my_list"].append(4) - 调用不改变结构的方法:
d.get("x")、d.setdefault("x", [])(后者只在键不存在时才插入,但它是原子操作,不会中断当前迭代)
真正容易被忽略的是:哪怕只在一个分支里写了 del d[k],整个循环就必须用快照方式启动——不能靠“我判断一下再删”来绕过限制。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










