Python字典创建、访问、修改、删除及遍历的完整指南
作者:念恒12306
在编程中,我们经常需要建立一种“映射关系”:比如通过学号找到学生姓名,通过商品编号找到价格。Python 的字典(dict)正是为此而生。它是一种可变、无序(Python 3.7+ 保持插入顺序)、键唯一的映射类型,通过哈希表实现 O(1) 的平均查找速度。
1. 字典是什么?
字典是键值对(key-value pair)的集合。每个键对应一个值,键必须是不可变类型(如字符串、数字、元组),值可以是任意类型。
# 一个简单的字典:学生信息
student = {"name": "张三", "age": 20, "major": "计算机"}
print(student["name"]) # 输出:张三代码解析:
student = {"name": "张三", "age": 20, "major": "计算机"}:创建一个名为student的字典,包含三个键值对。键"name"对应值"张三",键"age"对应整数20,键"major"对应字符串"计算机"。print(student["name"]):通过方括号语法使用键"name"访问其对应的值,输出"张三"。
核心特性:
- 键唯一:同一个字典中不能有重复的键。如果重复赋值,后一个会覆盖前一个。
- 可变:可以增、删、改键值对。
- 无序 → 有序:在 Python 3.6 之前是无序的,3.7 之后插入顺序被保留(作为语言特性)。这意味着遍历字典时会按照键值对添加的顺序返回。
- 高效查找:基于哈希表,平均时间复杂度 O(1)。
C/C++ 联动:
- C++ 中实现类似映射可以使用
std::unordered_map<Key, Value>(哈希表,平均 O(1))或std::map<Key, Value>(红黑树,O(log n))。Python 字典的底层就是哈希表,因此与unordered_map更相似。 - C++ 示例:
#include <unordered_map>
#include <string>
std::unordered_map<std::string, int> student = {{"name", 20}, {"age", 20}};
// 访问:student["name"] 或 student.at("name")- 区别:Python 字典的键可以是任意不可变类型(如字符串、数字、元组),而 C++
unordered_map的键类型需要在编译期确定,且必须提供哈希函数(内置类型已提供)。
2. 创建字典
2.1 使用花括号{}
# 空字典
empty = {}
# 带初始值
person = {"name": "李四", "age": 25}代码解析:
empty = {}:一对花括号创建了一个空字典。注意:空花括号是字典,不是集合(集合需要用set()创建空集)。person = {"name": "李四", "age": 25}:花括号内用逗号分隔多个键值对,每个键值对格式为"key": value。键可以是字符串、数字等不可变类型,值任意。
2.2 使用dict()构造函数
# 从关键字参数创建
person = dict(name="王五", age=30)
# 从可迭代对象(如列表的元组对)创建
items = [("name", "赵六"), ("age", 28)]
person = dict(items) # {'name': '赵六', 'age': 28}
# 空字典
empty = dict()代码解析:
dict(name="王五", age=30):关键字参数形式,键名会被当作字符串(不需要引号),这是创建字典的简洁方式。注意:键名必须是合法的变量名(不能以数字开头,不能含空格)。dict(items):items是一个列表,每个元素是一个包含两个元素的元组(或列表)。dict()将每个元组的第一个元素作为键,第二个作为值,构造字典。dict():无参数调用创建一个空字典,与{}等效。
C/C++ 联动:
- C++ 中初始化
unordered_map可以使用初始化列表:
std::unordered_map<std::string, int> person = {{"name", 30}, {"age", 25}};- 从键值对序列构造:可以遍历 vector 并插入,没有 Python 的
dict(iterable)那样直接。
2.3 使用zip()组合两个序列
keys = ["a", "b", "c"]
values = [1, 2, 3]
d = dict(zip(keys, values)) # {'a': 1, 'b': 2, 'c': 3}代码解析:
zip(keys, values):将两个列表打包成一系列元组:[('a',1), ('b',2), ('c',3)]。dict(...):将这个元组列表转换为字典。如果两个序列长度不同,zip会以较短的为准。
2.4 字典推导式(见7 )
3. 访问字典
3.1 通过键访问([])
d = {"apple": 5, "banana": 3}
print(d["apple"]) # 5
# print(d["orange"]) # KeyError: 'orange'代码解析:
d["apple"]:使用方括号和键名获取对应的值。如果键不存在,会抛出KeyError异常,程序终止(除非捕获异常)。因此这种方法适合确定键一定存在的场景。
C/C++ 联动:
- C++ 中
unordered_map的operator[]行为与 Python 不同:如果键不存在,它会插入一个默认构造的值并返回引用,不会抛出异常。例如:
std::unordered_map<std::string, int> d; int val = d["orange"]; // 插入键 "orange" 并值为 0,val = 0
- 若希望像 Python 一样不插入新键,可以使用
d.find(key)或d.at(key)(at会抛出std::out_of_range)。
3.2 使用get()方法(安全访问)
print(d.get("orange")) # None(不报错)
print(d.get("orange", 0)) # 0(指定默认值)代码解析:
d.get("orange"):尝试获取键"orange"的值。如果键不存在,返回None(不会抛出异常)。适合不确定键是否存在的场景。d.get("orange", 0):第二个参数指定默认值,当键不存在时返回0而不是None。
C/C++ 联动:C++ 中无直接对应的 get 方法,通常使用 find 并检查迭代器:
auto it = d.find("orange");
int val = (it != d.end()) ? it->second : 0;3.3 获取所有键、值、键值对
d = {"a": 1, "b": 2}
print(d.keys()) # dict_keys(['a', 'b'])
print(d.values()) # dict_values([1, 2])
print(d.items()) # dict_items([('a', 1), ('b', 2)])代码解析:
d.keys()、d.values()、d.items()返回的是视图对象(view)。这些视图动态反映字典的变化,如果字典后续修改,视图也会相应改变。- 视图对象支持迭代,可以用于
for循环。例如for k in d.keys():。 - 在 Python 3 中,视图不是列表,但可以转换为列表:
list(d.keys())。
C/C++ 联动:C++ 中获取所有键需要手动遍历或使用 range-based for:
for (const auto& pair : d) {
// pair.first 是键,pair.second 是值
}3.4 setdefault()—— 获取值,若键不存在则设置默认值
d = {"a": 1}
v = d.setdefault("b", 100) # 键 "b" 不存在,设置 d["b"]=100,并返回 100
print(d) # {'a': 1, 'b': 100}
v2 = d.setdefault("a", 99) # 键存在,返回原值 1,不修改代码解析:
setdefault先检查键是否存在,如果存在则返回对应的值;如果不存在,则插入key: default键值对,并返回default。这个方法常用于需要初始化缺失键的场景(比如构建分组字典)。
C/C++ 联动:C++ 中可以用 insert 配合 try_emplace 实现类似功能,但不如 Python 简洁。
4. 修改字典
4.1 添加或更新键值对
d = {}
d["name"] = "张三" # 添加
d["age"] = 25 # 添加
d["age"] = 26 # 更新代码解析:
- 当方括号内的键不存在时,该赋值操作会在字典中添加一个新的键值对。
- 当键已存在时,赋值会更新该键对应的值。
C/C++ 联动:C++ 中 unordered_map 的 operator[] 同样可添加或更新,但区别在于:如果键不存在,会插入默认构造的值再赋值。例如 d["age"] = 25 会先检查,若不存在则插入 int()(即0),再赋值为 25。因此 C++ 中不能直接用 [] 判断是否存在。
4.2 update()—— 合并另一个字典或可迭代对象
d1 = {"a": 1, "b": 2}
d2 = {"b": 3, "c": 4}
d1.update(d2) # d1 变为 {'a': 1, 'b': 3, 'c': 4}
# 也支持关键字参数:d1.update(c=5, d=6)代码解析:
update()接受一个字典或键值对的可迭代对象(如列表的元组)。它会将参数中的每个键值对合并到原字典中,如果键已存在则覆盖,如果不存在则添加。- 常见的用法:
d1.update(d2)相当于for k, v in d2.items(): d1[k] = v。
C/C++ 联动:C++ 中合并两个 unordered_map 需要循环插入,没有直接方法。
5. 删除字典元素
5.1 del语句
d = {"a": 1, "b": 2, "c": 3}
del d["b"] # 删除键为 "b" 的项
# del d["x"] # KeyError 若键不存在代码解析:
del d["b"]删除字典中键为"b"的键值对。如果键不存在,抛出KeyError。del也可以删除整个字典变量:del d,之后字典对象被回收。
5.2 pop()—— 删除并返回指定键的值
value = d.pop("a") # 返回 1,d 变为 {'c': 3}
# d.pop("x") # KeyError
d.pop("x", None) # 不报错,返回 None代码解析:
pop(key)删除指定键并返回对应的值。如果键不存在,抛出KeyError。pop(key, default)提供默认值,当键不存在时返回default,不会抛出异常。
C/C++ 联动:C++ 中 unordered_map 的 erase 方法删除键并返回被删除元素的数量(0或1),不返回值。要获取值需先 find。
5.3 popitem()—— 删除并返回最后一个键值对(Python 3.7+ 按 LIFO)
d = {"a": 1, "b": 2}
key, value = d.popitem() # 返回 ('b', 2)(插入顺序的最后一个)代码解析:
- 在 Python 3.7+ 中,
popitem()删除并返回字典中最后插入的键值对(后进先出)。在 3.6 之前,由于字典无序,它返回任意一个键值对。 - 返回值为一个元组
(key, value),可直接拆包。
5.4 clear()—— 清空字典
d.clear() # {}代码解析:删除字典中所有键值对,使字典变为空字典 {}。
6. 遍历字典
6.1 遍历键
for key in d:
print(key)
# 或 for key in d.keys():代码解析:直接使用 for key in d 是遍历字典最常用的方式,每次迭代获取一个键。也可以用 d.keys() 显式获取键视图。
6.2 遍历值
for value in d.values():
print(value)代码解析:d.values() 返回值的视图,用于遍历所有值。注意:值可能重复,且无法直接通过值找到对应的键(除非反向查找)。
6.3 同时遍历键和值
for key, value in d.items():
print(key, value)代码解析:d.items() 返回键值对视图,每个元素是一个 (key, value) 元组。通过拆包可以直接获取键和值。
6.4 带条件或转换的遍历
# 只打印值大于 10 的项
for k, v in d.items():
if v > 10:
print(k, v)代码解析:可以在循环体内加入条件判断,过滤出满足条件的键值对。
7. 字典推导式
与列表推导式类似,用于快速生成字典。
# 平方映射
squares = {x: x**2 for x in range(5)} # {0:0, 1:1, 2:4, 3:9, 4:16}代码解析:基本语法 {key_expr: value_expr for variable in iterable}。这里 x 是迭代变量,键是 x,值是 x**2。
# 条件过滤:只保留偶数键
even_squares = {x: x**2 for x in range(10) if x % 2 == 0}代码解析:在推导式末尾加上 if 子句,只对满足条件的 x 生成键值对。
# 交换键和值
original = {"a": 1, "b": 2}
inverted = {v: k for k, v in original.items()} # {1: 'a', 2: 'b'}代码解析:遍历原字典的 items(),将键和值互换作为新字典的键值对。注意:如果原字典有重复的值,后面的会覆盖前面的,因为键必须唯一。
8. 嵌套字典
字典的值可以是另一个字典,用于表示层次化数据。
students = {
"S001": {"name": "Alice", "score": 85},
"S002": {"name": "Bob", "score": 92}
}
print(students["S001"]["name"]) # Alice代码解析:
- 外层字典的键是学号,值是一个内层字典(包含
name和score)。 - 访问时使用两层方括号:先通过学号获取内层字典,再通过
"name"获取姓名。
安全访问嵌套:逐层使用 get 或使用 try-except。
9. 字典的常用方法汇总
| 方法 | 说明 |
|---|---|
get(key, default) | 安全获取值,键不存在时返回默认值(默认 None)。 |
setdefault(key, default) | 如果键存在,返回其值;否则设置 d[key]=default 并返回 default。 |
update([other]) | 用另一个字典或键值对序列更新当前字典。 |
keys() | 返回所有键的视图。 |
values() | 返回所有值的视图。 |
items() | 返回所有 (键, 值) 对的视图。 |
pop(key, default) | 删除指定键并返回其值,若不存在返回 default(若未提供则 KeyError)。 |
popitem() | 删除并返回最后一个插入的键值对(Python 3.7+)。 |
clear() | 清空字典。 |
copy() | 浅拷贝。 |
fromkeys(iterable, value) | 类方法,从可迭代对象创建字典,所有值初始为 value。 |
10. 字典的性能与底层原理
10.1 哈希表
Python 字典底层使用哈希表(hash table)。每个键通过哈希函数计算出一个整数索引,用于快速定位存储位置。因此查找、插入、删除的平均时间复杂度为 O(1),但最坏情况(哈希冲突严重)可能退化为 O(n)。Python 通过开放地址法和伪随机探测解决冲突,并自动调整表的大小(rehash)来维持高效。
C/C++ 联动:
- C++
std::unordered_map同样基于哈希表,平均 O(1),最坏 O(n)。两者都会在负载因子过高时自动 rehash。 - C++
std::map基于红黑树,时间复杂度 O(log n),但内存占用相对稳定,且键有序。 - Python 字典的哈希函数对字符串和整数做了优化,而 C++ 需要为自定义类型特化
std::hash。
10.2 键的要求
- 键必须是可哈希的:即对象必须实现
__hash__()和__eq__()方法,且在其生命周期中哈希值不变。 - 不可变类型(字符串、数字、元组)通常可哈希;列表、字典、集合等可变对象不可哈希。
- 元组中若包含可变对象,则该元组不可哈希。
# 合法键
d = {("a", 1): "tuple"}
# 非法键
# d = {["a", 1]: "list"} # TypeError: unhashable type: 'list'代码解析:
- 元组
("a", 1)中的元素都是不可变类型,因此元组本身可哈希,可以作为键。 - 列表
["a", 1]是可变的,不能作为键。
C/C++ 联动:C++ 中 unordered_map 的键也需要可哈希。内置类型已有标准哈希,自定义类型需要提供 hash 特化和 operator==。例如:
struct MyKey {
int a;
std::string b;
bool operator==(const MyKey& other) const { return a==other.a && b==other.b; }
};
namespace std {
template<> struct hash<MyKey> {
size_t operator()(const MyKey& k) const {
return hash<int>()(k.a) ^ (hash<string>()(k.b) << 1);
}
};
}10.3 内存占用
字典比列表占用更多内存(约 2~3 倍),因为哈希表需要预留额外空间。
11. 扩展:collections 模块中的字典变种
11.1 defaultdict—— 带默认工厂的字典
当访问不存在的键时,自动创建默认值,避免 KeyError
from collections import defaultdict
# 默认值为整数 0
dd = defaultdict(int)
dd["a"] += 1 # 不需要先检查 "a" 是否存在
print(dd) # {'a': 1}代码解析:
defaultdict(int)创建了一个默认值为int()(即 0)的字典。当访问不存在的键时,自动调用int()生成默认值并插入。dd["a"] += 1:由于"a"不存在,defaultdict先设置dd["a"] = 0,然后执行dd["a"] = 0 + 1。
# 默认值为空列表
dd2 = defaultdict(list)
dd2["group"].append(10) # 自动创建列表
print(dd2) # {'group': [10]}代码解析:list 工厂函数会在键缺失时创建一个空列表,然后可以安全地 append。
常见工厂:int, list, set, str, 或自定义函数。
C/C++ 联动:C++ 中 unordered_map 的 operator[] 在键不存在时会插入默认值(值类型的默认构造)。例如 std::unordered_map<std::string, int> m; m["a"] += 1; 会先插入 int()(0),再自增。这与 defaultdict(int) 行为类似,但 Python 的 defaultdict 可以灵活指定任何工厂函数(如 list),而 C++ 的默认构造只适用于值类型。
11.2 Counter—— 计数器
用于统计可哈希元素出现的次数。
from collections import Counter
colors = ["red", "blue", "red", "green", "blue", "red"]
cnt = Counter(colors)
print(cnt) # Counter({'red': 3, 'blue': 2, 'green': 1})
print(cnt["red"]) # 3
print(cnt.most_common(2)) # [('red', 3), ('blue', 2)]代码解析:
Counter(colors)遍历列表,统计每个元素出现的次数,返回一个类似字典的对象。cnt["red"]返回'red'出现的次数。对于不存在的键,返回 0(不报错)。most_common(2)返回出现次数最多的前 2 个元素及其次数,按降序排列。
C/C++ 联动:C++ 中没有 Counter 的直接对应,但可以用 unordered_map 实现计数,再借助 vector 排序获取最值。Python 的 Counter 更高级,内置了 most_common 等便捷方法。
11.3 OrderedDict—— 保持插入顺序(Python 3.7+ 中普通 dict 已有序)
在 Python 3.6 之前,字典无序,OrderedDict 用于保持插入顺序。现在普通 dict 也保持顺序,但 OrderedDict 还额外提供了 move_to_end() 等方法。
from collections import OrderedDict
od = OrderedDict()
od["a"] = 1
od["b"] = 2
od.move_to_end("a") # 将 "a" 移到末尾代码解析:move_to_end(key, last=True) 将指定键移动到末尾(如果 last=False 则移动到开头)。
C/C++ 联动:C++ 中保持插入顺序没有标准容器直接支持。可以使用 std::vector 配合 unordered_map 来记录顺序,或使用 boost::multi_index。Python 3.7+ 普通 dict 已有序,这是比 C++ 方便的特性。
11.4 ChainMap—— 将多个字典合并成一个视图
用于查找时按顺序搜索多个字典。
from collections import ChainMap
d1 = {"a": 1, "b": 2}
d2 = {"b": 3, "c": 4}
chain = ChainMap(d1, d2)
print(chain["b"]) # 从 d1 找到 2(取第一个)代码解析:
ChainMap(d1, d2)创建了一个虚拟的合并字典,优先查找d1,如果d1中没有再到d2中查找。- 修改
chain会影响第一个字典d1。
C/C++ 联动:C++ 无直接对应,但可用多个 map 和自定义搜索逻辑实现。
12. 字典与列表、元组的对比
| 特性 | 字典 | 列表 | 元组 |
|---|---|---|---|
| 容器类型 | 映射(键值对) | 序列 | 序列 |
| 有序性 | Python 3.7+ 插入有序 | 有序 | 有序 |
| 可变性 | 可变 | 可变 | 不可变 |
| 索引方式 | 键(不可变类型) | 整数索引 | 整数索引 |
| 查找速度 | 平均 O(1) | O(n) | O(n) |
| 内存占用 | 较大 | 中等 | 略小于列表 |
| 典型用途 | 关联数据、快速查找 | 有序序列 | 不可变序列 |
13. 字典的应用实战
13.1 统计字符串中字符出现次数
text = "hello world"
freq = {}
for ch in text:
if ch != " ":
freq[ch] = freq.get(ch, 0) + 1
print(freq)代码解析:
- 遍历字符串中的每个字符。
- 跳过空格。
freq.get(ch, 0)获取字符当前的计数,如果不存在则返回 0,然后加 1 并赋值。- 更优雅的方式:使用
Counter(text)。
13.2 使用字典实现简单缓存(记忆化)
def fibonacci(n, cache={}):
if n in cache:
return cache[n]
if n <= 1:
return n
cache[n] = fibonacci(n-1) + fibonacci(n-2)
return cache[n]代码解析:
- 默认参数
cache={}在函数定义时创建一次,后续调用会复用同一个字典对象(注意:可变默认参数要小心使用,但在这里正好用来实现缓存)。 - 每次计算
fibonacci(n)时,先检查缓存,如果已有结果则直接返回,避免重复递归。
13.3 根据值排序字典
d = {"apple": 3, "banana": 1, "cherry": 2}
# 按值升序排序,得到键的列表
sorted_keys = sorted(d, key=d.get) # ['banana', 'cherry', 'apple']
# 按值降序排序,得到键值对列表
sorted_items = sorted(d.items(), key=lambda x: x[1], reverse=True)代码解析:
sorted(d, key=d.get):d.get作为键函数,对每个键获取对应的值,按值排序。结果返回键的列表。sorted(d.items(), key=lambda x: x[1]):d.items()返回(key, value)元组,lambda x: x[1]取元组的第二个元素(即值)作为排序依据。
13.4 合并两个字典(Python 3.9+ 使用|)
d1 = {"a": 1, "b": 2}
d2 = {"b": 3, "c": 4}
merged = d1 | d2 # {'a':1, 'b':3, 'c':4}
d1 |= d2 # 原地更新(Python 3.9+)代码解析:
|运算符返回一个新字典,包含两个字典的键值对,重复键以右边字典为准。|=是原地操作,相当于d1.update(d2)。
C/C++ 联动:C++ 中合并两个 unordered_map 无运算符重载,需手动循环插入。
14. 常见陷阱与注意事项
| 陷阱 | 说明 | 解决方案 |
|---|---|---|
| 使用可变对象作为键 | 列表、字典等不可哈希,会报错 | 改用元组或字符串 |
| 在遍历字典时修改字典 | 会导致 RuntimeError: dictionary changed size during iteration | 遍历 list(d.keys()) 或 d.copy().items() |
混淆 get 和 setdefault | setdefault 会修改字典,get 不会 | 按需选择 |
| 认为字典总是无序(旧代码) | Python 3.7+ 插入顺序被保证,但依赖此特性的代码需要最低版本 | 使用 OrderedDict 若需严格兼容低版本 |
在 defaultdict 中传递可变对象 | 工厂函数使用 list 而非 list(),传递可调用对象 | defaultdict(list) 正确,defaultdict(list()) 错误 |
拷贝字典时使用 = 而非 copy | 二者指向同一对象,修改会互相影响 | 浅拷贝用 d.copy(),深拷贝用 copy.deepcopy |
C/C++ 联动:
- C++ 中同样有遍历时删除导致迭代器失效的问题,通常使用
for (auto it = m.begin(); it != m.end(); ) { if (cond) it = m.erase(it); else ++it; }。 - C++ 的
unordered_map默认不保留插入顺序,如果需要顺序需额外维护。
以上就是Python字典创建、访问、修改、删除及遍历的完整指南的详细内容,更多关于Python字典操作指南的资料请关注脚本之家其它相关文章!
