字典 Dict 的使用

字典是 Python 里另一个”灵魂容器”。它把”键 → 值”的映射用最直观的方式呈现出来,几乎所有 Python 项目都离不开它——JSON、配置、缓存、计数器、图的邻接表,能用字典表达的场景多到数不清。这一篇要把字典讲透:创建、增删改查、遍历、常用方法、Python 3.7+ 顺序保证、以及新手最容易踩的键类型和默认值陷阱。

一、什么是字典

字典(dict)是一个 可变 的、键值对(key-value) 集合。用大括号加冒号创建:

1
2
3
empty = {}
person = {"name": "咖飞", "age": 3, "city": "北京"}
config = {"host": "localhost", "port": 8080, "debug": True}

关键约束:

  • key 必须是可哈希的:字符串、数字、元组(内部也是不可变)都行;list/dict/set 不行;
  • key 唯一:同名 key 后写的覆盖先写的;
  • value 什么都行:任意 Python 对象。

二、创建字典的多种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
# 字面量
d1 = {"a": 1, "b": 2}

# dict() 构造函数
d2 = dict(a=1, b=2) # 关键字参数
d3 = dict([("a", 1), ("b", 2)]) # 元组列表
d4 = dict(zip(["a", "b"], [1, 2])) # zip 出来

# 字典推导式(下一节讲)
d5 = {c: ord(c) for c in "abc"} # {'a': 97, 'b': 98, 'c': 99}

# 从 keys 构造相同 value
d6 = dict.fromkeys(["a", "b", "c"], 0) # {'a': 0, 'b': 0, 'c': 0}

陷阱dict.fromkeys(["a", "b"], []) 会让所有 key 指向同一个空列表!要独立列表用推导式:{k: [] for k in keys}

三、增删改查

1
2
3
4
5
6
7
8
9
10
d = {"name": "咖飞", "age": 3}

print(d["name"]) # 咖飞
print(d["gender"]) # KeyError!

print(d.get("gender")) # None,没有 key 返回 None
print(d.get("gender", "未知")) # 未知,可指定默认值

print("name" in d) # True
print("gender" not in d) # True

规则:确定 key 存在用 d[k],可能不存在用 d.get(k)。前者更快,后者更安全。

改与增

1
2
3
4
5
d["age"] = 4              # 修改已有 key
d["gender"] = "unknown" # 新增 key

d.update({"age": 5, "city": "上海"}) # 批量更新
d.update(age=6, city="广州") # 关键字参数形式

Python 3.9+ 还有合并运算符 |

1
2
3
4
a = {"x": 1, "y": 2}
b = {"y": 20, "z": 30}
print(a | b) # {'x': 1, 'y': 20, 'z': 30} b 覆盖 a
a |= b # 就地更新

1
2
3
4
5
6
7
d = {"a": 1, "b": 2, "c": 3}

del d["a"] # 找不到会 KeyError
d.pop("b") # 返回 2 并删除
d.pop("z", None) # 找不到不报错,返回 None
d.popitem() # 弹出并返回最后插入的 (key, value)
d.clear() # 清空

四、遍历字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
score = {"张三": 90, "李四": 85, "王五": 78}

# 遍历 key(默认)
for name in score:
print(name)

# 显式遍历 keys
for name in score.keys():
print(name)

# 遍历 value
for s in score.values():
print(s)

# 同时遍历 key 和 value(最常用)
for name, s in score.items():
print(name, s)

keys / values / items 是视图

它们不是列表,是动态视图——字典变,它们也跟着变:

1
2
3
4
d = {"a": 1, "b": 2}
keys = d.keys()
d["c"] = 3
print(keys) # dict_keys(['a', 'b', 'c']),视图自动看到新 key

需要真正的列表用 list(d.keys())

注意:遍历字典时不能修改字典结构(增删 key):

1
2
3
for k in d:
if d[k] == 1:
del d[k] # RuntimeError!

正确做法:遍历 list(d) 或用推导式重建:

1
d = {k: v for k, v in d.items() if v != 1}

五、Python 3.7+ 有序性保证

Python 3.7 起,dict 官方保证按插入顺序遍历

1
2
3
4
5
d = {}
d["c"] = 1
d["a"] = 2
d["b"] = 3
list(d) # ['c', 'a', 'b'] 顺序保留

3.6 里 CPython 已经这样实现了,但只是”实现细节”;3.7 才写进语言规范。之前需要有序字典要用 collections.OrderedDict,现在不需要了,除非你需要 OrderedDict 独有的 move_to_end 方法。

六、字典常用方法速查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
d = {"a": 1, "b": 2}

d.keys() # dict_keys(['a', 'b'])
d.values() # dict_values([1, 2])
d.items() # dict_items([('a', 1), ('b', 2)])

d.get(k, default) # 查(不抛异常)
d.pop(k, default) # 删(不抛异常)
d.setdefault(k, default) # k 存在返回值,不存在则设为 default 并返回

# setdefault 常用于分组
groups = {}
for x in items:
groups.setdefault(x.type, []).append(x)

七、字典推导式

推导式(第 15 篇讲)也适用于字典:

1
2
3
4
5
6
7
8
9
# 平方表
squares = {n: n*n for n in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

# 反转 key 和 value
inv = {v: k for k, v in d.items()}

# 带条件
adults = {name: age for name, age in people.items() if age >= 18}

八、嵌套字典

字典的值可以是任何对象,包括字典本身:

1
2
3
4
5
6
7
users = {
"u1": {"name": "咖飞", "age": 3},
"u2": {"name": "小王", "age": 20},
}

print(users["u1"]["name"]) # 咖飞
users["u1"]["age"] = 4

深层嵌套的取值和赋值容易出 KeyError,几种应对:

1
2
3
4
5
6
7
8
9
10
# 1. 逐层 get
name = users.get("u1", {}).get("name", "匿名")

# 2. try
try:
x = users["u1"]["profile"]["email"]
except KeyError:
x = None

# 3. defaultdict 或专门的库(jmespath / dpath)

九、defaultdict:自动生成缺失值

collections.defaultdict 是字典的增强版:访问不存在的 key 时自动创建

1
2
3
4
5
6
7
8
9
10
11
from collections import defaultdict

# 分组
groups = defaultdict(list)
for name, dept in employees:
groups[dept].append(name) # 不用 setdefault

# 计数
counter = defaultdict(int)
for word in words:
counter[word] += 1 # 不用先判空

十、Counter:计数专用字典

collections.Counter 是专为计数设计的字典:

1
2
3
4
5
6
7
8
9
10
11
12
13
from collections import Counter

words = ["a", "b", "a", "c", "b", "a"]
c = Counter(words)
print(c) # Counter({'a': 3, 'b': 2, 'c': 1})
print(c.most_common(2)) # [('a', 3), ('b', 2)]
print(c["z"]) # 0,不存在返回 0 而不是 KeyError

# 加减、交集、并集
c1 = Counter("apple")
c2 = Counter("banana")
print(c1 + c2) # 元素级求和
print(c1 & c2) # 取共同的最小计数

十一、字典作为高性能查找表

字典的 x in dd[x] 都是 平均 O(1),比列表的线性查找快得多。所以大量存在性判断或映射查找请优先用字典或集合。

1
2
3
4
5
6
7
8
9
10
# ❌ O(n * m):慢
result = []
for x in big_list:
for y in small_list:
if x == y:
result.append(x)

# ✅ O(n + m):快
lookup = set(small_list)
result = [x for x in big_list if x in lookup]

十二、性能与内存

  • 每个 dict 起步内存约 200-300 字节;
  • 存储 1 亿个 int→int 大约 5-10GB,别指望在 8GB 内存里塞进去;
  • Python 3.6 起 dict 内存布局做过优化,比早期版本省 20%-25%;
  • 大量小 dict 场景可以考虑 dataclass(slots) 或者 namedtuple。

十三、一个综合例子:词频统计器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# wordfreq.py
"""统计一段文本中的词频,输出 Top N。"""

import re
from collections import Counter


def tokenize(text: str) -> list[str]:
"""粗糙分词:抽取所有连续字母。中文用户请上 jieba。"""
return re.findall(r"[a-zA-Z一-龥]+", text.lower())


def top_n(text: str, n: int = 5) -> list[tuple[str, int]]:
words = tokenize(text)
return Counter(words).most_common(n)


def stats(text: str) -> dict:
words = tokenize(text)
counter = Counter(words)
return {
"total_words": len(words),
"unique_words": len(counter),
"top_5": counter.most_common(5),
"longest": max(words, key=len) if words else "",
}


if __name__ == "__main__":
sample = """
Python is great. Python is powerful. Python is easy.
Python 是伟大的编程语言,Python 简单易学。
"""
print(stats(sample))

十四、常见陷阱

  1. 可变对象做 keyd[[1, 2]] = "x" → TypeError,list 不可哈希。用元组 (1, 2)
  2. 同一 key 被覆盖{"a": 1, "a": 2} 结果是 {"a": 2},不会警告。
  3. dict.fromkeys(..., []) 共享列表:改一个动全部。
  4. 遍历中修改字典:会抛 RuntimeError。遍历 list(d)d.copy()
  5. 拷贝浅d2 = d1.copy() 是浅拷贝,嵌套字典/列表要用 copy.deepcopy
  6. Python 3.7 前顺序不保证:老代码依赖顺序的话检查一下 Python 版本。
  7. in 只查 key:判断 value 是否存在要 x in d.values()

十五、小结与延伸阅读

  • 字典是最常用的容器之一,几乎所有映射场景都用它;
  • key 必须可哈希,value 任意;
  • get / setdefault / update / pop 是最常用的方法;
  • Python 3.7+ 保证插入顺序;
  • keys/values/items 是视图,不是列表;
  • 遍历中不要修改字典结构;
  • collections.defaultdict / Counter / OrderedDict 是字典家族的强力扩展;
  • 大批量查找永远用 dict/set 而不是 list。

延伸阅读:

下一篇 集合 Set 与集合运算 我们讲字典的兄弟——集合。