1. 字典是什么? #
- 字典(dict)存储键值对(
key: value),通过键快速取值,而不是像列表那样用数字下标。 - 全栈开发里字典几乎无处不在:HTTP 请求体与响应 JSON、
settings配置、ORM 序列化结果等。 - 本质都是「键 → 值」的映射。
- 键须为不可变且唯一的类型(常用字符串、数字、元组);值可以是任意类型,包括列表、嵌套字典。
- Python 3.7+ 会保持插入顺序,遍历
items()时顺序稳定。 - 逻辑上仍应把字典当作「按键查找」的结构,而非按位置访问的列表。
# 创建用户信息字典
user = {
"id": 1,
"name": "Alice",
"active": True,
}2. 创建与访问 #
- 空字典写
{}或dict();有初始键值时用字面量{"name": "Bob"}。 d["key"]在键不存在时抛KeyError,适合「字段必须存在」的场景。d.get("key")在键不存在时返回None,还可传默认值,如d.get("page", 1)。- 处理接口可选字段时优先用
get。 - 新增键用
d["x"] = value即可;只有「读取」才需要区分[]与get。
# 空字典
empty = {}
# 创建含姓名和年龄的字典
person = {"name": "Bob", "age": 25}
# 直接访问:键不存在会 KeyError
print(person["name"])
# get:更安全,键不存在返回 None
print(person.get("email"))
# get 可设默认值,键不存在时返回 ""
print(person.get("email", ""))3. 修改与删除 #
- 对已存在的键赋值会覆盖原值;对新键赋值会新增条目。
update()可一次合并多个键值,传入另一个字典或关键字参数。- Python 3.9+ 支持
d1 | d2得到新字典(键冲突时以后者为准),不改变d1。 - 若要在原字典上合并,仍用
d1.update(d2)。 - 删除用
del d["key"](键不存在会报错),或d.pop("key", default)删除并返回值。 pop适合「取出并删掉」的语义,如处理一次性 token。
# 初始字典
person = {"name": "Bob", "age": 25}
# 修改已有键的值
person["age"] = 26
# 新增键值对
person["city"] = "Beijing"
# 批量更新多个键
person.update({"job": "dev"})
print(person)
# 另一个字典,用于合并
person2 = {"salary": 10000}
# 合并两个字典(Python 3.9+)
print(person | person2)
# 删除指定键
del person["city"]
# 删除键并返回值,无键时返回 None
role = person.pop("job", None)
print(role)4. 遍历 #
- 遍历字典最常用
for key, value in d.items(),同时拿到键和值。 - 只关心键时写
for key in d(等价于for key in d.keys());只要值用d.values()。 - 判断某个键是否存在用
"key" in d,比先get再判断None更直观。 - 遍历顺序在 Python 3.7+ 为插入顺序,但不要依赖「键的字母序」。
- 需要排序展示时用
sorted(d.items())或按业务字段排序。
# 初始字典
person = {"name": "Bob", "age": 25}
# 同时遍历键和值
for key, value in person.items():
print(key, value)
# 默认只遍历键
for key in person:
print(key)
# 判断键是否存在
if "name" in person:
print("有 name 字段")5. 常用模式 #
- 实际项目里反复出现:用
get做计数累加、解析嵌套 JSON、列表按 id 建索引、JSON 互转、浅拷贝与深拷贝。 - 掌握这些模式后,读接口文档、写 DRF Serializer、处理配置会顺手很多。
- 下面分小节给出最小示例。
5.1 安全累加计数 #
- 统计词频、点击次数、状态分布时,键可能第一次出现。
- 用
d[key] = d.get(key, 0) + 1可避免KeyError,是字典里最经典的惯用写法之一。
# 初始化空字典用于计数
word_count = {}
# 遍历单词列表,累加每个单词出现次数
for word in ["apple", "banana", "apple"]:
word_count[word] = word_count.get(word, 0) + 1
print(word_count)5.2 嵌套字典(接口 JSON 常见) #
- 接口返回的 JSON 解析后往往是多层字典:外层
code、message,内层data里才是业务对象。 - 取值用链式
response["data"]["user_id"];任一层键缺失都会KeyError。 - 生产代码应对可选层用
get或先判断键是否存在。
# 模拟接口返回的嵌套 JSON 结构
response = {
"code": 200,
"data": {"user_id": 10, "name": "Alice"},
}
# 通过两层键访问 user_id
print(response["data"]["user_id"])5.3 列表转字典(按 id 索引) #
- 接口常返回
[{id: 1, ...}, {id: 2, ...}]的列表,按 id 遍历查找是 $O(n)$。 - 用字典推导式
{u["id"]: u for u in users}建成 id → 对象的映射。 - 之后
by_id[2]为 $O(1)$ 查找,是「不要用列表当下标字典」的正面写法。
# 用户列表,每个元素是一个字典
users = [{"id": 1, "name": "A"}, {"id": 2, "name": "B"}]
# 以 id 为键,用户字典为值,便于按 id 快速查找
by_id = {u["id"]: u for u in users}
print(by_id[2]["name"])5.4 JSON ↔ dict #
- 网络传输与存储多用 JSON 字符串;Python 内存里用 dict 操作更方便。
json.dumps(d)把字典序列化为字符串;json.loads(s)把字符串解析回字典。- 写 API、读配置文件、打日志时都会用到;
dumps会自动处理键的双引号格式。
# 导入 json 模块
import json
# dict 转 JSON 字符串
d = {"name": "Tom", "age": 20}
s = json.dumps(d)
print(s)
# JSON 字符串转 dict
d2 = json.loads(s)
print(d2)5.5 浅拷贝和深拷贝 #
- 赋值
d2 = d1共享同一对象;修改d2会影响d1。 d1.copy()或dict(d1)是浅拷贝:最外层是新字典,嵌套的内层对象仍可能共享。copy.deepcopy(d1)递归复制所有层级,副本完全独立。简单扁平字典用
copy()即可;含嵌套结构且要改内层时再用deepcopy。浅拷贝:
# 导入 copy 模块
import copy
# 含嵌套列表的字典
d1 = {"a": 1, "b": [2, 3]}
# 浅拷贝,只复制最外层
d2 = d1.copy()
# 修改副本中的嵌套列表
d2["b"].append(4)
# 原字典的内层列表也被修改,因为浅拷贝共享嵌套对象
print(d1)
print(d2)- 深拷贝:
# 导入 copy 模块
import copy
# 含嵌套列表的字典
d1 = {"a": 1, "b": [2, 3]}
# 深拷贝,递归复制所有层级
d2 = copy.deepcopy(d1)
# 修改副本中的嵌套列表
d2["b"].append(4)
# 原字典不受影响
print(d1)
print(d2)- 总结:简单字典用
copy()做浅拷贝。 - 包含嵌套结构(如列表、字典等)要独立副本用
deepcopy()。
6. 项目开发要点 #
- 字典是业务数据的默认容器。
- 读可选字段用
get:避免KeyError,如payload.get("page", 1)。 - 不要用列表当下标字典:需要按 id 查用户用
{id: user},不要反复遍历列表。 - JSON ↔ dict:
json.loads()/json.dumps()与字典互转。 - 合并字典:
d1.update(d2)或 Python 3.9+ 的d1 | d2(键冲突时以后者为准)。 - 浅拷贝:
copy()只复制一层,嵌套结构改内层仍可能影响原对象。 - 不要用可变对象当键:列表、字典不能作为 key。