【Python入门】6.4数据容器--字典(dict)
Python 字典(Dictionary)完全学习指南
字典(dict)是 Python 中唯一的映射型数据结构,核心特性是通过「键(key)-值(value)」对存储数据,查询效率极高(底层基于哈希表实现)。它是 Python 中最常用、最灵活的数据类型之一,适用于存储关联型数据(如配置信息、用户信息、映射关系等)。
本文将从基础定义→核心操作→进阶特性→应用场景→常见坑,系统讲解字典的全部知识,适合 Python 初学者循序渐进学习。
一、字典的基本概念
1. 核心特性
- 映射关系:每个 key 唯一对应一个 value(key 不可重复,value 可重复)。
- key 约束:key 必须是「不可变类型」(如整数、字符串、元组、布尔值),不能是列表、字典等可变类型(会触发
TypeError)。 - value 无约束:value 可以是任意 Python 类型(整数、字符串、列表、字典、函数等)。
- 无序性(Python 3.7 前):Python 3.7 之前字典是无序的,3.7+ 后保证插入顺序(但不依赖索引访问)。
- 动态性:字典创建后可随时增删改键值对,无需指定固定长度。
2. 与列表(list)的区别
| 特性 | 字典(dict) | 列表(list) |
|---|---|---|
| 访问方式 | 按 key 访问 | 按索引访问 |
| 元素关系 | 键值对映射 | 有序元素集合 |
| 查找效率 | 极高(O(1)) | 较低(O(n)) |
| 元素唯一性 | key 唯一,value 可重复 | 元素可重复 |
| 元素类型约束 | key 必须不可变,value 无约束 | 元素无类型约束 |
二、字典的创建
Python 提供多种创建字典的方式,根据场景选择即可:
1. 字面量方式(最常用)
用 {} 包裹键值对,键值对之间用逗号 , 分隔,key 和 value 用冒号 : 连接。
# 基础字典(字符串key)
person = {"name": "张三", "age": 20, "gender": "男"}
# 整数key(适合映射关系)
score = {1: 90, 2: 85, 3: 95}
# 混合类型key(不推荐,但语法允许)
mixed_dict = {True: "布尔值", (1, 2): "元组key", 3.14: "浮点数"}
# 空字典(后续动态添加元素)
empty_dict = {}
2. dict() 构造函数
适合从其他数据结构(如列表、元组)转换为字典,或动态传入键值对。
# 方式1:传入键值对参数
person = dict(name="张三", age=20, gender="男") # 键无需引号(本质是关键字参数)
# 方式2:传入二维可迭代对象(每个子元素是长度为2的序列)
list_data = [["name", "李四"], ("age", 22), ["gender", "女"]]
person2 = dict(list_data) # 结果:{'name': '李四', 'age': 22, 'gender': '女'}
# 方式3:传入两个可迭代对象(用zip()配对键和值)
keys = ["name", "age", "gender"]
values = ["王五", 25, "男"]
person3 = dict(zip(keys, values)) # zip(keys, values) 生成键值对迭代器
3. 字典推导式(高效创建)
类似列表推导式,通过表达式快速生成字典(适合数据转换/过滤)。
# 示例1:生成「数字→平方」的字典
square_dict = {x: x**2 for x in range(1, 6)} # 结果:{1:1, 2:4, 3:9, 4:16, 5:25}
# 示例2:过滤偶数的平方
even_square = {x: x**2 for x in range(1, 6) if x % 2 == 0} # 结果:{2:4, 4:16}
# 示例3:转换现有字典(修改key/value)
person = {"name": "张三", "age": 20}
upper_person = {k.upper(): v for k, v in person.items()} # 结果:{'NAME': '张三', 'AGE': 20}
4. fromkeys() 方法(创建默认值字典)
快速创建一个「所有 key 对应相同默认值」的字典(注意:默认值是共享引用,可变类型需谨慎)。
# 方式1:key列表 + 默认值(不可变类型,安全)
keys = ["a", "b", "c"]
dict1 = dict.fromkeys(keys, 0) # 结果:{'a':0, 'b':0, 'c':0}
# 方式2:key列表 + 可变类型(危险!所有key共享同一个列表)
dict2 = dict.fromkeys(keys, []) # 所有value指向同一个空列表
dict2["a"].append(1) # 修改一个key的value,所有key的value都会变
print(dict2) # {'a': [1], 'b': [1], 'c': [1]}(坑点!)
# 解决可变类型共享问题:用字典推导式
dict3 = {k: [] for k in keys} # 每个key对应独立的空列表
dict3["a"].append(1)
print(dict3) # {'a': [1], 'b': [], 'c': []}(正确)
三、字典的核心操作(增删改查)
字典的核心操作围绕「键(key)」展开,所有操作都通过 key 定位元素。
1. 查:访问字典的值
有 3 种常用方式,推荐使用 get() 方法(避免 KeyError)。
(1)按 key 直接访问(dict[key])
- 若 key 存在,返回对应 value;
- 若 key 不存在,直接触发
KeyError(程序崩溃),不推荐直接使用。
person = {"name": "张三", "age": 20}
print(person["name"]) # 输出:张三
print(person["height"]) # 报错:KeyError: 'height'
(2)get() 方法(推荐)
语法:dict.get(key, default=None)
- 若 key 存在,返回对应 value;
- 若 key 不存在,返回默认值(默认是
None,可自定义),不报错。
person = {"name": "张三", "age": 20}
print(person.get("name")) # 输出:张三
print(person.get("height")) # 输出:None(无报错)
print(person.get("height", 175)) # 输出:175(自定义默认值)
(3)setdefault() 方法(查询+默认赋值)
语法:dict.setdefault(key, default=None)
- 若 key 存在,返回对应 value;
- 若 key 不存在,给 key 赋值默认值并返回该默认值(会修改原字典)。
person = {"name": "张三", "age": 20}
print(person.setdefault("name", "李四")) # 输出:张三(key存在,不修改)
print(person.setdefault("height", 175)) # 输出:175(key不存在,添加到字典)
print(person) # {'name': '张三', 'age': 20, 'height': 175}
2. 增:添加键值对
有 2 种常用方式,本质都是「给不存在的 key 赋值」。
(1)直接赋值(dict[key] = value)
若 key 不存在,则添加新键值对;若 key 存在,则修改原有 value(见「改」操作)。
person = {"name": "张三", "age": 20}
person["height"] = 175 # 添加新key:height
person["gender"] = "男" # 添加新key:gender
print(person) # {'name': '张三', 'age': 20, 'height': 175, 'gender': '男'}
(2)update() 方法(批量添加/修改)
语法:dict.update(other_dict/iterable/key-value)
- 接收字典、二维可迭代对象或键值对参数,批量添加/修改键值对;
- 若 key 已存在,覆盖 value;若不存在,新增键值对。
person = {"name": "张三", "age": 20}
# 方式1:传入字典
person.update({"height": 175, "gender": "男"})
print(person) # {'name': '张三', 'age': 20, 'height': 175, 'gender': '男'}
# 方式2:传入键值对参数
person.update(height=180, city="北京") # 覆盖height,新增city
print(person) # {'name': '张三', 'age': 20, 'height': 180, 'gender': '男', 'city': '北京'}
# 方式3:传入二维可迭代对象
person.update([("job", "工程师"), ("salary", 15000)])
print(person) # 新增job和salary
3. 改:修改已有键的值
修改逻辑和「添加」一致:给已存在的 key 重新赋值。
person = {"name": "张三", "age": 20}
person["age"] = 21 # key已存在,修改value
person.update({"height": 175, "age": 22}) # update也可修改已有key
print(person) # {'name': '张三', 'age': 22, 'height': 175}
4. 删:删除键值对
有 4 种常用方式,根据是否需要返回删除的 value 选择。
(1)del 语句(直接删除,无返回值)
语法:del dict[key]
- 若 key 存在,删除该键值对;
- 若 key 不存在,触发
KeyError。
person = {"name": "张三", "age": 20, "height": 175}
del person["height"] # 删除height键值对
print(person) # {'name': '张三', 'age': 20}
del person["gender"] # 报错:KeyError: 'gender'
(2)pop() 方法(删除并返回 value)
语法:dict.pop(key, default)
- 若 key 存在,删除该键值对并返回 value;
- 若 key 不存在,返回默认值(无默认值则触发
KeyError)。
person = {"name": "张三", "age": 20, "height": 175}
age = person.pop("age") # 删除age,返回20
print(age) # 20
print(person) # {'name': '张三', 'height': 175}
# 不存在的key,指定默认值
gender = person.pop("gender", "未知")
print(gender) # 未知(无报错)
(3)popitem() 方法(删除最后插入的键值对)
语法:dict.popitem()
- Python 3.7+ 中,删除「最后插入」的键值对;3.7 前删除随机键值对;
- 返回被删除的键值对(元组形式
(key, value)); - 空字典调用会触发
KeyError。
person = {"name": "张三", "age": 20, "height": 175}
last_item = person.popitem() # 删除最后插入的height
print(last_item) # ('height', 175)
print(person) # {'name': '张三', 'age': 20}
(4)clear() 方法(清空字典)
语法:dict.clear()
- 删除字典中所有键值对,字典变为空字典(保留字典对象本身)。
person = {"name": "张三", "age": 20}
person.clear()
print(person) # {}
四、字典的遍历(迭代)
字典的遍历可针对「key」「value」「键值对」三种维度,使用 for 循环即可。
1. 遍历所有 key(默认遍历方式)
person = {"name": "张三", "age": 20, "gender": "男"}
for key in person: # 等价于 for key in person.keys()
print(key) # 输出:name, age, gender(顺序与插入一致)
2. 遍历所有 value
用 values() 方法获取所有 value 的迭代器:
for value in person.values():
print(value) # 输出:张三, 20, 男
3. 遍历所有键值对(最常用)
用 items() 方法获取所有键值对的迭代器(每个元素是 (key, value) 元组):
for key, value in person.items(): # 解包元组(推荐)
print(f"{key}: {value}")
# 输出:
# name: 张三
# age: 20
# gender: 男
# 不解包的写法(不推荐)
for item in person.items():
print(item) # 输出:('name', '张三'), ('age', 20), ('gender', '男')
遍历注意事项
- 遍历过程中不能直接删除/添加键值对(会改变字典大小,触发
RuntimeError); - 若需遍历中修改,可先复制字典的 key 或 items(如
for key in list(person.keys())):person = {"name": "张三", "age": 20, "gender": "男"} # 遍历中删除key(需先转成列表) for key in list(person.keys()): if key == "age": del person[key] print(person) # {'name': '张三', 'gender': '男'}(正确)
五、字典的常用方法(进阶)
除了增删改查,字典还有以下高频实用方法:
| 方法名 | 功能描述 |
|---|---|
keys() | 返回所有 key 的视图对象(类似列表,可迭代,3.7+ 有序) |
values() | 返回所有 value 的视图对象(可迭代) |
items() | 返回所有键值对的视图对象(可迭代) |
copy() | 浅拷贝字典(创建新字典,key 和不可变 value 共享,可变 value 仍引用同一对象) |
fromkeys(seq, val) | 用序列 seq 中的元素作为 key,val 作为默认 value 创建新字典(见创建部分) |
clear() | 清空字典 |
get(key, def) | 按 key 查询 value,不存在返回默认值 |
setdefault(key, def) | 查询 key,不存在则赋值默认值并返回 |
pop(key, def) | 删除 key 并返回 value,不存在返回默认值 |
popitem() | 删除最后插入的键值对并返回 |
update(...) | 批量添加/修改键值对 |
重点方法详解
1. 视图对象(keys()/values()/items())
- 视图对象(view objects)是动态的:原字典修改后,视图对象会同步更新;
- 视图对象不可直接修改,但可迭代、转换为列表(
list())。
person = {"name": "张三", "age": 20}
keys = person.keys()
print(keys) # dict_keys(['name', 'age'])(视图对象)
# 原字典修改,视图对象同步更新
person["gender"] = "男"
print(keys) # dict_keys(['name', 'age', 'gender'])
# 转换为列表(静态,不再同步)
key_list = list(keys)
person["height"] = 175
print(key_list) # ['name', 'age', 'gender'](不包含height)
2. 字典拷贝(copy() vs 深拷贝)
字典的 copy() 是浅拷贝(shallow copy):
- 若 value 是不可变类型(如整数、字符串),拷贝后完全独立;
- 若 value 是可变类型(如列表、字典),拷贝后仍共享该可变对象(修改一个会影响另一个)。
# 浅拷贝示例(可变value共享问题)
person = {"name": "张三", "hobbies": ["篮球", "游戏"]}
person_copy = person.copy() # 浅拷贝
person_copy["name"] = "李四" # 不可变value,修改不影响原字典
person_copy["hobbies"].append("读书") # 可变value,修改影响原字典
print(person) # {'name': '张三', 'hobbies': ['篮球', '游戏', '读书']}(hobbies被修改)
print(person_copy) # {'name': '李四', 'hobbies': ['篮球', '游戏', '读书']}
解决浅拷贝问题:深拷贝(copy.deepcopy())
需导入 copy 模块,深拷贝会递归复制所有层级的对象,完全独立。
import copy
person = {"name": "张三", "hobbies": ["篮球", "游戏"]}
person_deepcopy = copy.deepcopy(person) # 深拷贝
person_deepcopy["hobbies"].append("读书") # 修改可变value,不影响原字典
print(person) # {'name': '张三', 'hobbies': ['篮球', '游戏']}(原字典不变)
print(person_deepcopy) # {'name': '张三', 'hobbies': ['篮球', '游戏', '读书']}
六、字典的进阶特性
1. 嵌套字典(字典中包含字典)
实际开发中常用嵌套字典存储复杂数据(如多级配置、JSON 数据)。
# 嵌套字典示例:用户信息(包含地址子字典)
user = {
"id": 1001,
"name": "张三",
"info": {
"age": 20,
"gender": "男",
"address": {
"province": "广东",
"city": "深圳"
}
}
}
# 访问嵌套字典的值(链式访问)
print(user["info"]["age"]) # 20
print(user["info"]["address"]["city"]) # 深圳
# 修改嵌套字典的值
user["info"]["address"]["city"] = "广州"
print(user["info"]["address"]["city"]) # 广州
# 避免嵌套KeyError:多层get()
city = user.get("info", {}).get("address", {}).get("city", "未知")
print(city) # 广州(若中间key不存在,返回"未知")
2. 字典与 JSON 转换
字典是 Python 中与 JSON 格式最对应的结构(JSON 的 object 对应 Python 的 dict)。需使用 json 模块:
import json
# 字典转JSON字符串
person = {"name": "张三", "age": 20, "gender": "男"}
json_str = json.dumps(person, ensure_ascii=False) # ensure_ascii=False:保留中文
print(json_str) # {"name": "张三", "age": 20, "gender": "男"}
# JSON字符串转字典
json_str = '{"name": "李四", "age": 22, "gender": "女"}'
person_dict = json.loads(json_str)
print(person_dict) # {'name': '李四', 'age': 22, 'gender': '女'}
3. 字典的比较
字典比较只看键值对是否完全一致(与插入顺序无关,3.7+ 顺序不同但键值对相同也判定为相等)。
dict1 = {"a": 1, "b": 2}
dict2 = {"b": 2, "a": 1}
print(dict1 == dict2) # True(键值对相同,顺序无关)
print(dict1 != dict2) # False
dict3 = {"a": 1, "b": 3}
print(dict1 == dict3) # False(value不同)
七、字典的应用场景
字典的核心优势是「快速查找」和「关联映射」,常见应用场景:
- 存储配置信息:如数据库连接配置、程序参数。
config = { "host": "localhost", "port": 3306, "user": "root", "password": "123456", "database": "test" } - 数据统计:如统计字符串中字符出现次数(用键存字符,值存次数)。
s = "hello python" count = {} for char in s: count[char] = count.get(char, 0) + 1 # 不存在则默认0,+1后存入 print(count) # {'h':1, 'e':1, 'l':2, 'o':2, ' ':1, 'p':1, 'y':1, 't':1, 'n':1} - 替代多条件判断:用字典映射替代
if-elif-else,代码更简洁。# 传统if-elif def func(op): if op == "add": return 1 + 2 elif op == "sub": return 3 - 1 elif op == "mul": return 2 * 3 # 字典映射(更简洁) def func(op): op_dict = { "add": 1 + 2, "sub": 3 - 1, "mul": 2 * 3 } return op_dict.get(op, "无效操作") - 缓存数据:利用字典的 O(1) 查找效率,缓存计算结果(避免重复计算)。
cache = {} def calculate(x): if x in cache: # 已缓存,直接返回 return cache[x] result = x ** 2 # 未缓存,计算后存入 cache[x] = result return result
八、常见错误与避坑指南
1. KeyError(键不存在)
- 原因:用
dict[key]访问不存在的 key; - 解决:用
get()方法并指定默认值,或先判断 key 是否存在(if key in dict:)。person = {"name": "张三"} # 错误:print(person["age"]) # 正确: print(person.get("age", 20)) # 20 if "age" in person: print(person["age"])
2. key 必须是不可变类型
- 错误示例:用列表、字典作为 key(触发
TypeError); - 正确示例:用字符串、整数、元组(元素也需不可变)作为 key。
# 错误:dict1 = {[1,2]: "列表key"} # TypeError: unhashable type: 'list' # 正确:dict1 = {(1,2): "元组key"}
3. fromkeys() 可变默认值共享
- 原因:
fromkeys()的默认值是共享引用,可变类型(列表、字典)会被所有 key 共用; - 解决:用字典推导式替代
fromkeys()(见创建部分示例)。
4. 遍历中修改字典大小
- 原因:遍历字典时直接删除/添加键值对,改变字典长度;
- 解决:遍历字典的副本(如
list(dict.keys()))或先收集要修改的 key。person = {"name": "张三", "age": 20, "gender": "男"} # 错误:for key in person: del person[key](RuntimeError) # 正确:遍历key的列表副本 for key in list(person.keys()): del person[key] print(person) # {}
5. 浅拷贝导致的可变值共享
- 原因:
copy()是浅拷贝,嵌套的可变类型 value 仍共享; - 解决:需要完全独立的字典时,用
copy.deepcopy()。
九、总结
字典是 Python 中最核心的数据结构之一,核心要点:
- 本质是「键值对映射」,key 唯一且不可变,value 无约束;
- 核心优势是 O(1) 查找效率,远超列表;
- 常用操作:增(
dict[key]=val/update())、删(del/pop()/clear())、改(重新赋值)、查(get()推荐); - 进阶特性:嵌套字典、字典推导式、浅/深拷贝、JSON 转换;
- 避坑重点:避免 KeyError、不使用可变类型作为 key、注意
fromkeys()和浅拷贝的共享问题。
建议多通过「数据统计」「配置存储」等场景练习,熟练掌握字典的使用,它会成为你 Python 编程中最常用的工具之一!
小练习
- 用字典推导式生成 1-10 中奇数对应的立方字典(如
{1:1, 3:27, ...}); - 统计一个列表中各元素出现的次数(如
[1,2,2,3,3,3]→{1:1, 2:2, 3:3}); - 实现一个嵌套字典,存储 3 个学生的姓名、年龄、科目成绩(语文/数学/英语),并计算每个学生的平均分。
更多推荐


所有评论(0)