一、取值:三种方式
d = {'a': 1}
d['b'] # KeyError
d.get('b') # None
d.get('b', 0) # 0,给默认值
'b' in d # False,判断存在
经验:不确定键是否存在就用 get,并提供有意义的默认值。get('b', 0) 比 if 'b' in d: ... else: 0 简洁得多。
注意 d.get('b') or 0 与 d.get('b', 0) 的区别:如果 d['b'] 的值是 0、''、[] 这类假值,前者会错误地返回默认值。
二、setdefault:一次查找完成「取或建」
# 常见写法:两次查找
if key not in d:
d[key] = []
d[key].append(x)
# 更好
d.setdefault(key, []).append(x)
setdefault 只在键不存在时才插入默认值,已存在则直接返回现有值。做「按字段分组」时非常顺手:
groups = {}
for item in items:
groups.setdefault(item['city'], []).append(item)
注意:默认值 [] 每次调用都会先创建一个空列表(即使没用到),有轻微开销;defaultdict 用工厂函数,只在需要时创建。
三、defaultdict 更省心
from collections import defaultdict
groups = defaultdict(list)
for item in items:
groups[item['city']].append(item)
counts = defaultdict(int)
for w in words:
counts[w] += 1
defaultdict(list)、defaultdict(int)、defaultdict(set) 覆盖了绝大多数场景。它的「副作用」是:访问不存在的键会自动创建,所以 if k in d 判断要用 in,不能靠 d[k] 是否为 None。
四、合并字典的四种方式
a = {'x': 1, 'y': 2}
b = {'y': 3, 'z': 4}
{**a, **b} # {'x':1,'y':3,'z':4} 3.5+
a | b # 同上,3.9+
a.update(b) # 原地修改 a
dict(a, **b) # 键必须是字符串
后者键重复时右边的覆盖左边。要「合并时把值相加」得自己写循环或用 Counter。
五、顺序:3.7 起是插入顺序
Python 3.7+ 的 dict 保证按插入顺序遍历。所以「需要一个有序的键值容器」不用再找 OrderedDict。但要注意:
json.dumps默认保持插入顺序,如需固定顺序用sort_keys=True。- 用
popitem()删的是最后插入的(OrderedDict可指定last=False删最早的)。 - 判断两个字典相等不看顺序,只看键值对。
六、键的要求
键必须可哈希(hashable):
- 能用作键:
int、str、tuple(元素都可哈希)、frozenset。 - 不能:
list、dict、set。
d[(1, 2)] = 'ok' # 元组可以
d[[1, 2]] = 'no' # TypeError: unhashable type
另外,自定义对象作键时默认按 id 哈希,若要按内容相等,需实现 __hash__ 和 __eq__。
七、遍历与常用方法
for k in d: ... # 键
for k, v in d.items(): ... # 键值对(最常用)
for v in d.values(): ... # 值
d.keys() / d.values() / d.items() # 视图对象,随字典变化
d.pop('k', default) # 弹出,可给默认值
d.popitem() # 弹出最后一个
d.clear()
d.copy() # 浅拷贝
遍历时不能增删键,会抛 RuntimeError: dictionary changed size during iteration。要改就先 list(d.items()) 复制一份再遍历。
八、嵌套结构的读写
多层嵌套取值容易写出 d.get('a', {}).get('b', {}) 这种链式调用,可读性差。可以用 try/except KeyError 或第三方库(如 glom)。JSON 场景下更推荐先校验结构再取,避免深层 None 引发的 AttributeError。
处理接口返回的 JSON 时,用 JSON 格式化校验 先看清结构,再决定取值的容错策略。
小结
不确定键存在就用 get 带默认值;分组用 setdefault 或 defaultdict;合并用 {a, b} 或 a | b;键必须可哈希;遍历时不要增删键。