极简工具箱

免费在线工具 · 本地计算 · 无需上传

Python 字典:get、setdefault 与合并的四种写法

`d["key"]` 取不到会抛 KeyError,`d.get("key")` 返回 None——但真正让代码变简洁的是 `setdefault` 和 `defaultdict`。

广告位(百度联盟)

一、取值:三种方式

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。但要注意:

六、键的要求

键必须可哈希(hashable):

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;键必须可哈希;遍历时不要增删键。

本文相关工具(本地计算,无需上传)

更多使用教程