深入理解Python字典(Dictionary):从基础操作到高级应用
flyfish
一、Python 字典(Dictionary)基本概念
1. 定义
Python 字典是一种可变、无序的数据结构,用于存储键值对(key - value pairs)。每个键(key)必须是唯一的,且不可变(如字符串、数字或完全不可变的元组),而值(value)可以是任意类型,包括列表、字典等。
2. 特点
- 键的唯一性:字典中不允许有重复的键,如果尝试使用相同的键进行赋值,后面的值会覆盖前面的值。
- 无序性:在 Python 3.6 之前,字典是无序的,即键值对的存储顺序和插入顺序无关;从 Python 3.6 开始,字典会保持插入顺序,但这只是一个实现细节,在 Python 3.7 及以后版本才正式成为语言规范。
- 高效查找:通过键可以快速访问对应的值,平均时间复杂度为 O ( 1 ) O(1) O(1)。
3. 用途
字典常用于存储和检索具有关联关系的数据,例如配置信息、数据库记录、统计计数等。
二、使用方法
1. 字典的创建与初始化
- 使用花括号
# 创建一个空字典
empty_dict = {}
# 创建包含键值对的字典
person = {'name': 'Bob', 'age': 25, 'city': 'New York'}
- 使用
dict()
构造函数
# 从键值对元组列表创建字典
dict_from_list = dict([('a', 1), ('b', 2)])
# 从关键字参数创建字典
dict_from_kwargs = dict(name='Charlie', age=30)
# 使用 fromkeys 方法创建字典
keys = ('name', 'age', 'city')
new_dict = dict.fromkeys(keys, 'unknown') # 创建一个字典,所有值初始化为'unknown'
2. 字典的基本操作
- 访问元素
person = {'name': 'Bob', 'age': 25}
# 使用键访问值
print(person['name']) # 输出: Bob
# 使用 get() 方法安全访问值
print(person.get('city', 'Unknown')) # 输出: Unknown
# 使用 setdefault 方法设置默认值
city = person.setdefault('city', 'Unknown') # 如果'city'键不存在,则设置并返回'Unknown'
- 添加和修改元素
person = {'name': 'Bob', 'age': 25}
# 添加新的键值对
person['city'] = 'New York'
# 修改已有键的值
person['age'] = 26
- 删除元素
person = {'name': 'Bob', 'age': 25, 'city': 'New York'}
# 使用 del 语句删除键值对
del person['city']
# 使用 pop() 方法删除键值对并返回值
age = person.pop('age')
# 使用 popitem() 方法随机删除并返回一个键值对
last_item = person.popitem()
3. 字典的常用方法
keys()
、values()
和items()
person = {'name': 'Bob', 'age': 25}
# 获取所有键
print(person.keys()) # 输出: dict_keys(['name', 'age'])
# 获取所有值
print(person.values()) # 输出: dict_values(['Bob', 25])
# 获取所有键值对
print(person.items()) # 输出: dict_items([('name', 'Bob'), ('age', 25)])
update()
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
# 将 dict2 的键值对更新到 dict1 中
dict1.update(dict2)
print(dict1) # 输出: {'a': 1, 'b': 3, 'c': 4}
clear()
person = {'name': 'Bob', 'age': 25}
# 清空字典
person.clear()
print(person) # 输出: {}
4. 字典的遍历
- 遍历键
person = {'name': 'Bob', 'age': 25}
for key in person:
print(key)
- 遍历值
person = {'name': 'Bob', 'age': 25}
for value in person.values():
print(value)
- 遍历键值对
person = {'name': 'Bob', 'age': 25}
for key, value in person.items():
print(key, value)
5. 字典的嵌套
students = {
'Alice': {'age': 20, 'grades': [85, 90, 78]},
'Bob': {'age': 21, 'grades': [70, 80, 85]}
}
print(students['Alice']['grades']) # 输出: [85, 90, 78]
6. 字典的排序
student_scores = {'Alice': 85, 'Bob': 70, 'Charlie': 90}
# 按键排序
sorted_by_key = sorted(student_scores.items())
# 按值排序
sorted_by_value = sorted(student_scores.items(), key=lambda item: item[1])
三、理论
1. 数据结构基础 - 哈希表
Python 字典是基于哈希表(Hash Table)实现的。哈希表是一种根据键(key)直接访问内存存储位置的数据结构,它通过哈希函数(Hash Function)将键映射到存储桶(Bucket)或槽(Slot)中,以此实现高效的数据存储和查找。
2. 哈希函数
哈希函数是哈希表的核心,它接受一个键作为输入,然后返回一个固定范围的整数,这个整数就是键对应的哈希值(Hash Value)。在 Python 中,内置的 hash()
函数可以计算大多数不可变对象的哈希值。不同的键通过哈希函数计算得到的哈希值可能相同,这种情况称为哈希冲突(Hash Collision)。Python 字典使用开放寻址法(Open Addressing)来解决哈希冲突,即当发生冲突时,会在哈希表中寻找下一个可用的槽位。
3. 字典的存储和查找过程
- 存储过程
- 计算哈希值:使用哈希函数计算键的哈希值。
- 确定槽位:通过哈希值和哈希表的大小,确定键值对应该存储的槽位。
- 处理冲突:如果该槽位已经被占用(发生哈希冲突),则使用开放寻址法寻找下一个可用的槽位。
- 存储键值对:将键值对存储到找到的槽位中。
- 查找过程
- 计算哈希值:使用相同的哈希函数计算键的哈希值。
- 确定槽位:根据哈希值和哈希表的大小,找到对应的槽位。
- 检查键是否匹配:如果该槽位存储的键与查找的键相同,则返回对应的值;如果不相同,则使用开放寻址法继续查找下一个槽位,直到找到匹配的键或遍历完整个哈希表。
4. 动态扩容机制
为了保证哈希表的性能,Python 字典会在必要时进行动态扩容。当哈希表的填充因子(即已使用的槽位数与总槽位数的比值)超过一定阈值(通常是 2/3)时,字典会自动进行扩容操作:
- 创建新的哈希表:创建一个更大的哈希表,通常是原哈希表大小的两倍。
- 重新哈希:将原哈希表中的所有键值对重新计算哈希值,并插入到新的哈希表中。此过程可能会导致性能下降,因此选择合适的初始容量以减少扩容次数是很重要的。
5. 键的不可变性要求
Python 字典要求键必须是不可变对象(如字符串、数字、完全不可变的元组),这是因为哈希表的工作原理依赖于键的哈希值的稳定性。如果键是可变对象(如列表、字典),那么在对象状态改变后,其哈希值也会改变,这会导致在查找或删除键值对时出现错误。需要注意的是,即使元组可以用作字典的键,但如果元组内包含可变对象(例如列表),则整个元组也是不可作为字典键的,因为这样的元组不是完全不可变的。
四、完整示例代码
# 创建字典
student = {'name': 'Alice', 'age': 20, 'grades': [85, 90, 78]}
# 访问元素
print(student['name']) # 输出: Alice
print(student.get('city', 'Unknown')) # 输出: Unknown
city = student.setdefault('city', 'Los Angeles') # 设置默认城市
# 添加和修改元素
student['city'] = 'Los Angeles'
student['age'] = 21
# 删除元素
del student['grades']
age = student.pop('age')
# 使用常用方法
print(student.keys()) # 输出: dict_keys(['name', 'city'])
print(student.values()) # 输出: dict_values(['Alice', 'Los Angeles'])
print(student.items()) # 输出: dict_items([('name', 'Alice'), ('city', 'Los Angeles')])
# 遍历字典
for key, value in student.items():
print(key, value)
# 字典嵌套
students = {
'Alice': {'age': 20, 'grades': [85, 90, 78]},
'Bob': {'age': 21, 'grades': [70, 80, 85]}
}
print(students['Alice']['grades']) # 输出: [85, 90, 78]
# 字典排序
student_scores = {'Alice': 85, 'Bob': 70, 'Charlie': 90}
sorted_by_key = sorted(student_scores.items())
sorted_by_value = sorted(student_scores.items(), key=lambda item: item[1])
print(sorted_by_key)
print(sorted_by_value)
五、字典推导式(Dictionary Comprehension)
字典推导式(Dictionary Comprehension)是 Python 中一种简洁、高效的创建字典的方式,它允许你使用一行代码从可迭代对象(如列表、元组、集合等)中创建字典,其语法类似于列表推导式和集合推导式。
基本语法
字典推导式的基本语法结构如下:
{key_expression: value_expression for item in iterable if condition}
key_expression
:用于生成字典的键的表达式。value_expression
:用于生成字典的值的表达式。item
:可迭代对象中的每个元素。iterable
:可迭代对象,如列表、元组、集合等。if condition
:可选的条件表达式,用于筛选符合条件的元素,只有满足条件的元素才会被用于生成字典。
示例及解释
1. 简单的字典推导式
假设你有两个列表,一个存储学生的名字,另一个存储对应的成绩,你可以使用字典推导式将它们组合成一个字典。
names = ['Alice', 'Bob', 'Charlie']
scores = [85, 90, 78]
student_scores = {name: score for name, score in zip(names, scores)}
print(student_scores)
解释:
zip(names, scores)
将names
和scores
列表中的元素一一对应,形成元组。for name, score in zip(names, scores)
遍历这些元组,将每个元组中的第一个元素赋值给name
,第二个元素赋值给score
。{name: score}
以name
为键,score
为值创建字典的键值对。
2. 带有条件的字典推导式
假设你只想将成绩大于 80 分的学生信息存储到字典中,可以添加条件表达式。
names = ['Alice', 'Bob', 'Charlie']
scores = [85, 90, 78]
high_score_students = {name: score for name, score in zip(names, scores) if score > 80}
print(high_score_students)
解释:
if score > 80
是条件表达式,只有当score
大于 80 时,对应的name
和score
才会被添加到字典中。
3. 使用字典推导式对现有字典进行转换
假设你有一个字典,存储了学生的名字和成绩,你想将成绩转换为对应的等级(A: 90 - 100, B: 80 - 89, C: 70 - 79)。
student_scores = {'Alice': 85, 'Bob': 90, 'Charlie': 78}
grade_dict = {name: 'A' if score >= 90 else 'B' if score >= 80 else 'C' for name, score in student_scores.items()}
print(grade_dict)
解释:
student_scores.items()
返回字典的键值对元组。for name, score in student_scores.items()
遍历这些键值对。'A' if score >= 90 else 'B' if score >= 80 else 'C'
是一个三元表达式,根据成绩确定对应的等级。