字典(dict)是 Python 中最核心、最常用的数据结构之一,它以键值对(Key-Value Pair) 的形式存储数据,具备高效查找、灵活修改的特性,广泛应用于配置存储、数据映射、计数统计等场景。
一、字典的定义与核心特性
1. 基本定义
字典是一种可变、无序(Python 3.7+ 后变为插入有序)、可嵌套的数据类型,每个元素由 键(Key) 和 值(Value) 组成,格式为:
{key1: value1, key2: value2, ..., keyn: valuen}
- 键(Key):必须是不可变类型(如
str、int、tuple、bool),且唯一(重复键会覆盖前值); - 值(Value):可以是任意类型(如
int、str、list、dict甚至函数),无唯一性要求; - 分隔符:键与值用
:分隔,键值对之间用,分隔,整体用{}包裹。
2. 核心特性
| 特性 | 说明 |
|---|---|
| 可变(Mutable) | 可动态添加、修改、删除键值对(但键不可修改,需通过 “删除旧键 + 添加新键” 实现) |
| 插入有序(3.7+) | 3.7 版本后,字典会保留键值对的插入顺序,遍历顺序与插入顺序一致 |
| 高效查找 | 基于哈希表实现,查找键的时间复杂度为 O(1)(远快于列表的 O (n)) |
| 可嵌套 | 值可以是另一个字典,支持多层嵌套(如 {'a': {'b': 1}}) |
| 不可哈希 | 字典本身是可变类型,不可作为其他字典的键(但可作为值) |
二、字典的创建方式
Python 提供多种创建字典的方法,适用于不同场景:
1. 直接使用 {}(最常用)
适合已知固定键值对的场景:
# 基础字典(键为字符串、整数)
user = {"name": "Alice", "age": 25, "is_student": False}
# 键为元组(不可变类型,合法)
point = {(1, 2): "原点", (3, 4): "终点"}
# 键为布尔值(True=1,False=0,注意重复覆盖)
bool_dict = {True: "真", 1: "整数1"} # 结果:{True: "整数1"}(True与1哈希值相同,覆盖)
2. 使用 dict() 构造函数
适合从其他数据结构(如列表、元组)转换为字典:
# 1. 传入键值对参数(键无需引号,本质是关键字参数)
user = dict(name="Bob", age=30, is_student=True)
# 2. 传入可迭代对象(每个元素是长度为2的子迭代对象,如列表、元组)
# 方式1
list_data = [["name", "Charlie"], ["age", 28]]
user = dict(list_data) # 结果:{"name": "Charlie", "age": 28}
# 方式2
fields = ["name", "age"]
values = ["Charlie", 28]
user = dict(zip(fields, values))
# 3. 传入字典(用于复制,浅拷贝)
old_dict = {"a": 1}
new_dict = dict(old_dict) # 结果:{"a": 1}
3. 字典推导式(高效创建)
类似列表推导式,通过循环和条件过滤动态生成字典,简洁高效:
# 1. 基础推导式:键值对映射
numbers = [1, 2, 3]
square_dict = {num: num**2 for num in numbers} # 结果:{1:1, 2:4, 3:9}
# 2. 带条件过滤:只保留偶数的平方
even_square_dict = {num: num**2 for num in numbers if num % 2 == 0} # 结果:{2:4}
# 3. 双循环推导式:从两个列表生成字典
keys = ["a", "b"]
values = [10, 20]
pair_dict = {k: v for k, v in zip(keys, values)} # 结果:{"a":10, "b":20}
4. 使用 dict.fromkeys()(批量创建默认值)
适合批量创建键,且所有键的初始值相同的场景(注意:值是引用类型时,修改一个会影响所有):
# 1. 基础用法:值为不可变类型(安全)
keys = ["x", "y", "z"]
default_dict = dict.fromkeys(keys, 0) # 结果:{"x":0, "y":0, "z":0}
# 2. 注意:值为可变类型(如列表)时,所有键共享同一个列表
danger_dict = dict.fromkeys(keys, [])
danger_dict["x"].append(1) # 结果:{"x":[1], "y":[1], "z":[1]}(所有键的列表都被修改)
# 解决方法:用推导式避免共享引用
safe_dict = {k: [] for k in keys}
safe_dict["x"].append(1) # 结果:{"x":[1], "y":[], "z":[]}(仅修改x的列表)
三、字典的核心操作(增删改查)
1. 访问键值对(查)
字典的访问围绕 “键” 展开,有两种核心方式:
(1)通过 dict[key] 访问(直接访问)
- 优点:语法简洁;
- 缺点:若键不存在,会抛出
KeyError异常。
user = {"name": "Alice", "age": 25}
print(user["name"]) # 输出:Alice
print(user["gender"]) # 报错:KeyError: 'gender'
(2)通过 dict.get(key, default) 访问(安全访问)
- 优点:键不存在时返回
default(默认是None),不报错; - 场景:不确定键是否存在时(如处理用户输入、配置文件)。
print(user.get("name")) # 输出:Alice
print(user.get("gender", "未知")) # 输出:未知(键不存在,返回默认值)
print(user.get("gender")) # 输出:None(无默认值时返回None)
2. 添加 / 修改键值对(增 / 改)
字典的键是唯一的,因此 “添加” 和 “修改” 共用同一语法:
- 若键不存在:执行 “添加” 操作;
- 若键已存在:执行 “修改” 操作(覆盖旧值)。
user = {"name": "Alice", "age": 25}
# 1. 修改已有键(age)
user["age"] = 26 # 结果:{"name": "Alice", "age": 26}
# 2. 添加新键(gender)
user["gender"] = "female" # 结果:{"name": "Alice", "age": 26, "gender": "female"}
# 3. 批量添加/修改:使用 dict.update()
user.update({"city": "Beijing", "age": 27}) # 批量操作:修改age,添加city
print(user) # 输出:{"name": "Alice", "age": 27, "gender": "female", "city": "Beijing"}
# update() 也支持键值对参数
user.update(city="Shanghai", is_student=False)
print(user) # 输出:{"name": "Alice", "age": 27, "gender": "female", "city": "Shanghai", "is_student": False}
3. 删除键值对(删)
Python 提供多种删除字典元素的方法,适用于不同需求:
| 方法 | 作用 | 返回值 | 注意事项 |
|---|---|---|---|
del dict[key] |
删除指定键值对 | 无返回值 | 键不存在时抛 KeyError |
dict.pop(key) |
删除指定键值对并返回对应的值 | 返回被删除的值 | 键不存在时抛 KeyError(可加默认值:pop(key, default)) |
dict.popitem() |
删除最后一个键值对(3.7+ 插入顺序) | 返回被删除的键值对(元组) | 字典为空时抛 KeyError |
dict.clear() |
清空字典所有键值对 | 无返回值 | 字典变为空字典({}) |
user = {"name": "Alice", "age": 25, "gender": "female"}
# 1. del:删除指定键
del user["gender"]
print(user) # 输出:{"name": "Alice", "age": 25}
# 2. pop():删除并返回值
age = user.pop("age")
print(age) # 输出:25
print(user) # 输出:{"name": "Alice"}
# pop() 键不存在时用默认值
city = user.pop("city", "Unknown")
print(city) # 输出:Unknown(键不存在,返回默认值)
# 3. popitem():删除最后一个键值对
name_pair = user.popitem()
print(name_pair) # 输出:("name", "Alice")
print(user) # 输出:{}(空字典)
# 4. clear():清空字典
user = {"a": 1}
user.clear()
print(user) # 输出:{}
四、字典的遍历
字典的遍历可针对 “键”“值”“键值对” 三种维度,核心依赖 keys()、values()、items() 三个方法(返回的是视图对象,不占用额外内存,会随原字典动态更新)。
1. 遍历键(dict.keys())
user = {"name": "Alice", "age": 25}
# 方式1:直接遍历字典(默认遍历键)
for key in user:
print(key) # 输出:name, age
# 方式2:显式调用 keys()(更清晰)
for key in user.keys():
print(key) # 输出:name, age
2. 遍历值(dict.values())
for value in user.values():
print(value) # 输出:Alice, 25
3. 遍历键值对(dict.items())
最常用的遍历方式,返回键值对元组((key, value)),可通过解包直接获取键和值:
for key, value in user.items():
print(f"{key}: {value}") # 输出:name: Alice, age: 25
4. 遍历的效率对比
- 遍历键 / 值 / 键值对的时间复杂度均为 O(n)(n 为字典长度);
- 视图对象(
keys()/values()/items())不生成新列表,比list(dict.keys())更高效(避免内存浪费)。
五、字典的常用方法
除了上述增删改查和遍历方法,字典还有一些高频实用方法:
| 方法 | 作用 | 示例 |
|---|---|---|
dict.copy() |
浅拷贝字典(值为可变类型时仅复制引用) | new_dict = old_dict.copy() |
dict.setdefault(key, default) |
若键存在则返回值,不存在则添加键值对(默认值为 None) |
user.setdefault("city", "Beijing") |
dict.__contains__(key) |
判断键是否在字典中(等价于 key in dict) |
"name" in user(返回 True) |
1. dict.copy()(浅拷贝)
- 作用:创建字典的副本,修改副本不会影响原字典(但值为可变类型时,副本和原字典共享该值的引用);
- 深拷贝:若值包含嵌套字典 / 列表,需用
copy.deepcopy()(需导入copy模块)。
# 浅拷贝示例
old_dict = {"name": "Alice", "hobbies": ["reading", "running"]}
new_dict = old_dict.copy()
# 修改不可变类型(name):不影响原字典
new_dict["name"] = "Bob"
print(old_dict["name"]) # 输出:Alice
# 修改可变类型(hobbies):原字典也会被修改(共享引用)
new_dict["hobbies"].append("swimming")
print(old_dict["hobbies"]) # 输出:["reading", "running", "swimming"]
# 深拷贝(解决嵌套可变类型问题)
import copy
deep_new_dict = copy.deepcopy(old_dict)
deep_new_dict["hobbies"].append("dancing")
print(old_dict["hobbies"]) # 输出:["reading", "running", "swimming"](原字典不变)
选择哪种拷贝方式取决于你的字典是否包含嵌套的可变对象。如果只是简单的字典(值都是不可变类型),浅拷贝就足够了;如果有嵌套结构,可能需要深拷贝。
2. dict.setdefault()(添加默认值)
- 场景:确保某个键存在(若不存在则添加默认值),常用于统计场景(如单词计数)。
# 单词计数示例
words = ["apple", "banana", "apple"]
count_dict = {}
for word in words:
# 若word不存在,添加count_dict[word] = 0;若存在,返回当前值
count_dict.setdefault(word, 0)
count_dict[word] += 1
print(count_dict) # 输出:{"apple": 2, "banana": 1}
六、字典的进阶特性
1. 有序性(Python 3.7+)
- 3.7 版本前,字典是无序的,遍历顺序与插入顺序无关;
- 3.7 版本后,字典保证插入顺序,
popitem()固定删除最后一个插入的键值对; - 与
collections.OrderedDict的区别:3.7+ 普通字典的有序性已与OrderedDict一致,且性能更优(OrderedDict仅保留兼容旧代码和额外功能,如move_to_end())。
2. 键的要求(不可变 + 哈希 able)
字典的键必须满足 hashable(可哈希),即:
- 不可变类型:
str、int、float、tuple(元组内元素也需可哈希)、bool; - 不可用可变类型:
list、dict、set(这些类型不可哈希,无法作为键)。
# 合法键:元组(不可变)
valid_dict = {(1, 2): "valid"}
# 非法键:列表(可变)
invalid_dict = {[1, 2]: "invalid"} # 报错:TypeError: unhashable type: 'list'
3. 嵌套字典
字典的值可以是另一个字典,支持多层嵌套(常用于存储复杂结构数据,如用户信息、配置文件):
# 嵌套字典:用户信息(包含地址子字典)
user = {
"name": "Alice",
"age": 25,
"address": {
"city": "Beijing",
"street": "Main Road",
"zipcode": "100000"
}
}
# 访问嵌套值
print(user["address"]["city"]) # 输出:Beijing
# 修改嵌套值
user["address"]["zipcode"] = "100001"
print(user["address"]["zipcode"]) # 输出:100001
# 添加嵌套键
user["address"]["district"] = "Haidian"
print(user["address"]) # 输出:{"city": "Beijing", "street": "Main Road", "zipcode": "100001", "district": "Haidian"}
七、字典的性能与应用场景
1. 性能优势
- 查找快:基于哈希表实现,查找键的时间复杂度为 O (1)(列表查找需 O (n));
- 修改快:添加 / 删除键值对的时间复杂度为 O (1)(列表插入 / 删除需 O (n))。
2. 性能劣势
- 内存占用高:需存储键值对和哈希表结构,内存占用比列表大;
- 有序性开销:3.7+ 有序字典需维护插入顺序,比无序字典(3.6-)略慢(但日常场景可忽略)。
3. 典型应用场景
(1)配置参数存储
# 应用配置
config = {
"debug": True,
"timeout": 30,
"database": {
"host": "localhost",
"port": 3306,
"name": "mydb"
}
}
(2)数据计数统计
# 统计字符串中字符出现次数
text = "hello world"
char_count = {}
for char in text:
char_count[char] = char_count.get(char, 0) + 1
print(char_count) # 输出:{'h':1, 'e':1, 'l':3, 'o':2, ' ':1, 'w':1, 'r':1, 'd':1}
(3)函数关键字参数(**kwargs)
用于接收任意数量的关键字参数:
python
def print_user(**kwargs):
for key, value in kwargs.items():
print(f"{key}: {value}")
print_user(name="Alice", age=25, gender="female")
# 输出:
# name: Alice
# age: 25
# gender: female
(4)数据映射与转换
# 性别编码转换(1→男,2→女)
gender_code = {1: "男", 2: "女"}
user_data = [{"name": "Bob", "gender_code": 1}, {"name": "Lily", "gender_code": 2}]
# 转换为可读性别
for user in user_data:
user["gender"] = gender_code[user["gender_code"]]
del user["gender_code"]
print(user_data)
# 输出:[{"name": "Bob", "gender": "男"}, {"name": "Lily", "gender": "女"}]
八、注意事项
- 键的唯一性:重复添加相同键会覆盖旧值,如
{"a":1, "a":2}最终为{"a":2}; - 键的不可变类型:避免用列表、字典等可变类型作为键,否则会抛
TypeError; - 浅拷贝陷阱:
copy()和dict()是浅拷贝,嵌套可变值会共享引用,需用copy.deepcopy()深拷贝; - 空字典判断:判断字典是否为空用
if not dict(而非if dict == {}),更高效简洁; - 视图对象动态性:
keys()/values()/items()返回的视图对象会随原字典更新,如:d = {"a":1} keys = d.keys() d["b"] = 2 print(keys) # 输出:dict_keys(['a', 'b'])(视图对象自动更新)
九、与其他类似结构的对比
| 数据结构 | 特点 | 适用场景 |
|---|---|---|
字典(dict) |
键值对、无序(3.7 + 有序)、可变 | 高效查找、配置存储、计数统计 |
有序字典(OrderedDict) |
3.7- 保证有序,3.7+ 与普通字典一致 | 兼容旧代码、需 move_to_end() 等特殊功能 |
defaultdict(collections.defaultdict) |
键不存在时自动生成默认值(需指定类型) | 嵌套字典、批量统计(避免 get() 重复代码) |
列表(list) |
索引访问、有序、可变 | 有序数据存储、遍历(查找效率低) |
例如,defaultdict 简化嵌套统计:
from collections import defaultdict
# 用 defaultdict 统计每个单词的出现位置
text = "hello world hello python"
word_positions = defaultdict(list) # 键不存在时自动生成空列表
for idx, word in enumerate(text.split()):
word_positions[word].append(idx)
print(word_positions)
# 输出:defaultdict(<class 'list'>, {'hello': [0, 2], 'world': [1], 'python': [3]})
总结
字典是 Python 中功能最强大的数据结构之一,其键值对映射和高效查找特性使其成为处理结构化数据的首选。掌握字典的创建、增删改查、遍历和进阶用法,能极大提升代码的简洁性和效率。在实际开发中,需根据场景选择合适的字典类型(如普通字典、defaultdict),并注意键的不可变特性和浅拷贝陷阱。