Skip to content

字典与集合

字典(Dictionary)和集合(Set)是Python中两种重要的数据结构。字典用于存储键值对,集合用于存储不重复的元素。本章将详细介绍这两种数据结构的使用方法。

字典(Dictionary)

什么是字典

字典是Python中一种可变的、无序的键值对集合。每个键值对用冒号:分隔,键值对之间用逗号,分隔,整个字典用花括号{}包围。

python
# 创建一个简单的字典
# 字典存储的是键值对(key-value pair)
student = {
    "name": "张三",      # 键是"name",值是"张三"
    "age": 20,           # 键是"age",值是20
    "city": "北京"       # 键是"city",值是"北京"
}

print(student)  # 输出: {'name': '张三', 'age': 20, 'city': '北京'}
print(type(student))  # 输出: <class 'dict'>

字典的特点

python
# 字典的特点演示

# 1. 键必须是不可变类型(如字符串、数字、元组)
valid_dict = {
    "name": "张三",      # 字符串作为键 - 正确
    1: "数字键",         # 数字作为键 - 正确
    (1, 2): "元组键"     # 元组作为键 - 正确
}
print(valid_dict)

# 2. 键必须唯一,重复的键会覆盖
duplicate_dict = {
    "name": "张三",
    "name": "李四"       # 重复的键,后面的值会覆盖前面的
}
print(duplicate_dict)  # 输出: {'name': '李四'}

# 3. 值可以是任意类型
mixed_dict = {
    "string": "字符串",
    "number": 123,
    "list": [1, 2, 3],
    "dict": {"a": 1, "b": 2},
    "tuple": (1, 2, 3),
    "set": {1, 2, 3}
}
print(mixed_dict)

创建字典

python
# 创建字典的多种方式

# 方式1:使用花括号 {}
dict1 = {"name": "张三", "age": 20}
print(f"方式1: {dict1}")

# 方式2:使用 dict() 构造函数
dict2 = dict(name="李四", age=25)  # 使用关键字参数
print(f"方式2: {dict2}")

# 方式3:使用 dict() 传入键值对列表
dict3 = dict([("name", "王五"), ("age", 30)])
print(f"方式3: {dict3}")

# 方式4:创建空字典
empty_dict1 = {}
empty_dict2 = dict()
print(f"空字典1: {empty_dict1}, 类型: {type(empty_dict1)}")
print(f"空字典2: {empty_dict2}, 类型: {type(empty_dict2)}")

# 方式5:使用字典推导式
squares = {x: x**2 for x in range(1, 6)}
print(f"字典推导式: {squares}")  # 输出: {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}

# 方式6:使用 fromkeys() 创建具有相同值的字典
keys = ["a", "b", "c"]
dict6 = dict.fromkeys(keys, 0)  # 所有键的值都设为0
print(f"fromkeys: {dict6}")  # 输出: {'a': 0, 'b': 0, 'c': 0}

访问字典元素

python
# 访问字典中的值

student = {"name": "张三", "age": 20, "city": "北京"}

# 方式1:使用方括号 []
print(f"姓名: {student['name']}")   # 输出: 张三
print(f"年龄: {student['age']}")    # 输出: 20

# 注意:如果键不存在,会报错
# print(student['gender'])  # KeyError: 'gender'

# 方式2:使用 get() 方法(推荐)
print(f"姓名: {student.get('name')}")  # 输出: 张三

# get() 方法的优势:键不存在时返回 None,不会报错
print(f"性别: {student.get('gender')}")  # 输出: None

# 可以指定默认值
print(f"性别: {student.get('gender', '未知')}")  # 输出: 未知

# 方式3:获取所有键、值、键值对
print(f"所有键: {student.keys()}")      # 输出: dict_keys(['name', 'age', 'city'])
print(f"所有值: {student.values()}")    # 输出: dict_values(['张三', 20, '北京'])
print(f"所有键值对: {student.items()}")  # 输出: dict_items([('name', '张三'), ('age', 20), ('city', '北京')])

# 将它们转换为列表使用
keys_list = list(student.keys())
values_list = list(student.values())
items_list = list(student.items())
print(f"键列表: {keys_list}")
print(f"值列表: {values_list}")
print(f"键值对列表: {items_list}")

修改字典

python
# 修改字典内容

student = {"name": "张三", "age": 20}

# 1. 添加/修改单个键值对
student["city"] = "北京"       # 添加新键值对
student["age"] = 21            # 修改已有键的值
print(f"修改后: {student}")

# 2. 使用 update() 方法批量更新
student.update({
    "gender": "男",            # 添加新键
    "age": 22                  # 修改已有键
})
print(f"update后: {student}")

# update() 也可以传入关键字参数
student.update(email="zhangsan@example.com", phone="12345678901")
print(f"再次update后: {student}")

# 3. 删除元素
# 使用 del 关键字
del student["phone"]
print(f"删除phone后: {student}")

# 使用 pop() 方法(删除并返回值)
age = student.pop("age")
print(f"删除的年龄: {age}")
print(f"pop后: {student}")

# pop() 可以指定默认值,键不存在时不报错
result = student.pop("height", "不存在")
print(f"pop不存在的键: {result}")

# 4. 删除并返回最后一个键值对
student["age"] = 20
student["height"] = 175
last_item = student.popitem()
print(f"删除的最后一项: {last_item}")
print(f"popitem后: {student}")

# 5. 清空字典
student_copy = student.copy()
student_copy.clear()
print(f"清空后: {student_copy}")

字典的遍历

python
# 遍历字典的多种方式

student = {"name": "张三", "age": 20, "city": "北京"}

# 1. 遍历键(默认行为)
print("=== 遍历键 ===")
for key in student:
    print(f"键: {key}")

# 显式遍历键
print("\n=== 显式遍历键 ===")
for key in student.keys():
    print(f"键: {key}")

# 2. 遍历值
print("\n=== 遍历值 ===")
for value in student.values():
    print(f"值: {value}")

# 3. 遍历键值对(推荐)
print("\n=== 遍历键值对 ===")
for key, value in student.items():
    print(f"{key}: {value}")

# 4. 使用 enumerate 获取索引
print("\n=== 带索引遍历 ===")
for index, (key, value) in enumerate(student.items()):
    print(f"第{index + 1}个: {key} = {value}")

# 5. 在遍历时修改字典(需要创建副本)
print("\n=== 安全修改遍历 ===")
scores = {"张三": 85, "李四": 92, "王五": 78}
# 错误做法:直接遍历修改
# for name in scores:
#     if scores[name] < 80:
#         del scores[name]  # RuntimeError

# 正确做法:遍历副本
for name in list(scores.keys()):
    if scores[name] < 80:
        del scores[name]
print(f"删除低分后: {scores}")

字典常用方法

python
# 字典常用方法详解

# 1. setdefault() - 获取值,不存在则设置默认值
person = {"name": "张三"}

# 如果键存在,返回对应的值
name = person.setdefault("name", "默认名")
print(f"name: {name}")  # 输出: 张三

# 如果键不存在,添加键值对并返回默认值
age = person.setdefault("age", 20)
print(f"age: {age}")    # 输出: 20
print(f"person: {person}")  # 输出: {'name': '张三', 'age': 20}

# 2. copy() - 浅拷贝
original = {"a": 1, "b": [1, 2, 3]}
copied = original.copy()
copied["a"] = 100
copied["b"].append(4)  # 注意:列表是引用,会影响原字典

print(f"原字典: {original}")  # b的列表也被修改了
print(f"拷贝字典: {copied}")

# 3. 深拷贝
import copy
original2 = {"a": 1, "b": [1, 2, 3]}
deep_copied = copy.deepcopy(original2)
deep_copied["b"].append(4)

print(f"原字典(深拷贝): {original2}")  # 不受影响
print(f"深拷贝字典: {deep_copied}")

# 4. 判断键是否存在
student = {"name": "张三", "age": 20}

print(f"'name' in student: {'name' in student}")      # True
print(f"'city' in student: {'city' in student}")      # False
print(f"'name' not in student: {'name' not in student}")  # False

# 5. 获取字典长度
print(f"字典长度: {len(student)}")  # 输出: 2

# 6. 合并字典(Python 3.9+)
dict_a = {"a": 1, "b": 2}
dict_b = {"c": 3, "d": 4}

# 使用 | 运算符
merged = dict_a | dict_b
print(f"合并结果: {merged}")

# 使用 |= 运算符原地合并
dict_a |= dict_b
print(f"dict_a合并后: {dict_a}")

# Python 3.5+ 使用 ** 解包
dict_c = {"a": 1, "b": 2}
dict_d = {"c": 3, "d": 4}
merged_unpack = {**dict_c, **dict_d}
print(f"解包合并: {merged_unpack}")

嵌套字典

python
# 嵌套字典的使用

# 1. 字典中嵌套字典
students = {
    "张三": {
        "age": 20,
        "gender": "男",
        "scores": {
            "math": 90,
            "english": 85
        }
    },
    "李四": {
        "age": 21,
        "gender": "女",
        "scores": {
            "math": 88,
            "english": 92
        }
    }
}

# 访问嵌套数据
print(f"张三的年龄: {students['张三']['age']}")
print(f"张三的数学成绩: {students['张三']['scores']['math']}")

# 遍历嵌套字典
print("\n=== 学生信息 ===")
for name, info in students.items():
    print(f"\n学生: {name}")
    print(f"  年龄: {info['age']}")
    print(f"  性别: {info['gender']}")
    print(f"  成绩:")
    for subject, score in info['scores'].items():
        print(f"    {subject}: {score}")

# 2. 字典中嵌套列表
class_info = {
    "class_name": "一班",
    "students": ["张三", "李四", "王五"],
    "teachers": ["王老师", "李老师"]
}

print(f"\n班级: {class_info['class_name']}")
print(f"学生列表: {class_info['students']}")

# 添加新学生
class_info['students'].append("赵六")
print(f"添加后学生列表: {class_info['students']}")

集合(Set)

什么是集合

集合是一个无序的、不重复的元素集合。集合使用花括号{}创建,或者使用set()函数。

python
# 创建集合

# 方式1:使用花括号
fruits = {"苹果", "香蕉", "橙子"}
print(f"水果集合: {fruits}")
print(f"类型: {type(fruits)}")

# 方式2:使用 set() 函数
numbers = set([1, 2, 3, 4, 5])
print(f"数字集合: {numbers}")

# 方式3:创建空集合(注意:不能用 {})
empty_set = set()  # 正确
# empty_set = {}   # 错误!这会创建空字典
print(f"空集合: {empty_set}, 类型: {type(empty_set)}")

# 方式4:使用集合推导式
squares = {x**2 for x in range(1, 6)}
print(f"平方数集合: {squares}")

# 集合自动去重
duplicates = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4}
print(f"去重后: {duplicates}")  # 输出: {1, 2, 3, 4}

集合的特点

python
# 集合的特点演示

# 1. 元素不重复
set1 = {1, 2, 3, 2, 1}
print(f"自动去重: {set1}")  # 输出: {1, 2, 3}

# 2. 元素必须不可变
valid_set = {1, "hello", (1, 2)}  # 数字、字符串、元组都可以
print(f"有效集合: {valid_set}")

# 列表不能作为集合元素(因为列表是可变的)
# invalid_set = {1, 2, [3, 4]}  # TypeError

# 3. 集合是无序的
set2 = {3, 1, 4, 1, 5, 9}
print(f"无序集合: {set2}")  # 输出顺序可能不同

# 4. 不能通过索引访问
# print(set2[0])  # TypeError: 'set' object is not subscriptable

# 5. 集合本身是可变的,但可以创建不可变集合
frozen = frozenset([1, 2, 3])
print(f"不可变集合: {frozen}")
# frozen.add(4)  # AttributeError: 'frozenset' object has no attribute 'add'

集合的基本操作

python
# 集合的基本操作

fruits = {"苹果", "香蕉", "橙子"}

# 1. 添加元素
fruits.add("葡萄")
print(f"添加后: {fruits}")

# 添加已存在的元素不会有变化
fruits.add("苹果")
print(f"添加重复元素后: {fruits}")

# 2. 删除元素
# remove() - 元素不存在会报错
fruits.remove("香蕉")
print(f"remove后: {fruits}")

# discard() - 元素不存在不会报错
fruits.discard("西瓜")  # 不报错
print(f"discard后: {fruits}")

# pop() - 随机删除一个元素
popped = fruits.pop()
print(f"删除的元素: {popped}")
print(f"pop后: {fruits}")

# 3. 清空集合
fruits_copy = fruits.copy()
fruits_copy.clear()
print(f"清空后: {fruits_copy}")

# 4. 获取集合长度
print(f"集合长度: {len(fruits)}")

# 5. 判断元素是否存在
fruits.add("苹果")
fruits.add("香蕉")
print(f"'苹果' in fruits: {'苹果' in fruits}")      # True
print(f"'西瓜' in fruits: {'西瓜' in fruits}")      # False

集合的数学运算

python
# 集合的数学运算

A = {1, 2, 3, 4, 5}
B = {4, 5, 6, 7, 8}

# 1. 并集(union 或 |)
print(f"并集(union): {A.union(B)}")
print(f"并集(|): {A | B}")

# 2. 交集(intersection 或 &)
print(f"交集(intersection): {A.intersection(B)}")
print(f"交集(&): {A & B}")

# 3. 差集(difference 或 -)
print(f"A-B差集(difference): {A.difference(B)}")
print(f"A-B差集(-): {A - B}")
print(f"B-A差集: {B - A}")

# 4. 对称差集(symmetric_difference 或 ^)
# 对称差集 = 并集 - 交集
print(f"对称差集: {A.symmetric_difference(B)}")
print(f"对称差集(^): {A ^ B}")

# 5. 判断子集和超集
C = {1, 2, 3}
D = {1, 2, 3, 4, 5}

print(f"\nC是D的子集: {C.issubset(D)}")        # True
print(f"C <= D: {C <= D}")                      # True
print(f"C < D (真子集): {C < D}")               # True

print(f"D是C的超集: {D.issuperset(C)}")        # True
print(f"D >= C: {D >= C}")                      # True
print(f"D > C (真超集): {D > C}")               # True

# 6. 判断是否不相交
E = {1, 2, 3}
F = {4, 5, 6}
print(f"\nE和F不相交: {E.isdisjoint(F)}")      # True

G = {1, 2, 3}
H = {3, 4, 5}
print(f"G和H不相交: {G.isdisjoint(H)}")        # False(有公共元素3)

集合的更新操作

python
# 集合的更新操作(原地修改)

A = {1, 2, 3}
B = {3, 4, 5}

# 1. update() - 并集更新
A_copy = A.copy()
A_copy.update(B)
print(f"update后: {A_copy}")  # {1, 2, 3, 4, 5}

# 使用 |= 运算符
A_copy2 = A.copy()
A_copy2 |= B
print(f"|= 后: {A_copy2}")

# 2. intersection_update() - 交集更新
A_copy = A.copy()
A_copy.intersection_update(B)
print(f"intersection_update后: {A_copy}")  # {3}

# 使用 &= 运算符
A_copy2 = A.copy()
A_copy2 &= B
print(f"&= 后: {A_copy2}")

# 3. difference_update() - 差集更新
A_copy = A.copy()
A_copy.difference_update(B)
print(f"difference_update后: {A_copy}")  # {1, 2}

# 使用 -= 运算符
A_copy2 = A.copy()
A_copy2 -= B
print(f"-= 后: {A_copy2}")

# 4. symmetric_difference_update() - 对称差集更新
A_copy = A.copy()
A_copy.symmetric_difference_update(B)
print(f"symmetric_difference_update后: {A_copy}")  # {1, 2, 4, 5}

# 使用 ^= 运算符
A_copy2 = A.copy()
A_copy2 ^= B
print(f"^= 后: {A_copy2}")

集合推导式

python
# 集合推导式

# 基本语法:{expression for item in iterable if condition}

# 1. 创建平方数集合
squares = {x**2 for x in range(1, 6)}
print(f"平方数集合: {squares}")

# 2. 带条件的集合推导式
even_squares = {x**2 for x in range(1, 11) if x % 2 == 0}
print(f"偶数的平方: {even_squares}")

# 3. 字符串处理
text = "Hello World"
unique_chars = {char.lower() for char in text if char.isalpha()}
print(f"唯一字母: {unique_chars}")

# 4. 从列表创建集合并过滤
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
odd_set = {x for x in numbers if x % 2 != 0}
print(f"奇数集合: {odd_set}")

# 5. 嵌套推导式
# 创建所有可能的坐标点(去重)
points = {(x, y) for x in range(3) for y in range(3)}
print(f"坐标点集合: {points}")

集合的实际应用

python
# 集合的实际应用场景

# 1. 去除列表中的重复元素
numbers = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
unique_numbers = list(set(numbers))
print(f"去重后: {unique_numbers}")

# 保持原顺序的去重
from collections import OrderedDict
numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3]
unique_ordered = list(dict.fromkeys(numbers))
print(f"保持顺序去重: {unique_ordered}")

# 2. 查找两个列表的共同元素
list1 = [1, 2, 3, 4, 5]
list2 = [4, 5, 6, 7, 8]
common = list(set(list1) & set(list2))
print(f"共同元素: {common}")

# 3. 查找只在其中一个列表中的元素
only_in_list1 = list(set(list1) - set(list2))
only_in_list2 = list(set(list2) - set(list1))
print(f"只在list1中: {only_in_list1}")
print(f"只在list2中: {only_in_list2}")

# 4. 判断列表是否有重复元素
def has_duplicates(lst):
    return len(lst) != len(set(lst))

print(f"[1,2,3]有重复: {has_duplicates([1, 2, 3])}")      # False
print(f"[1,2,2,3]有重复: {has_duplicates([1, 2, 2, 3])}")  # True

# 5. 权限验证
user_permissions = {"read", "write", "execute"}
required_permissions = {"read", "write"}

# 检查用户是否拥有所有必需权限
has_all = required_permissions.issubset(user_permissions)
print(f"拥有所有必需权限: {has_all}")

# 检查用户是否拥有任意一个必需权限
has_any = bool(user_permissions & required_permissions)
print(f"拥有任意必需权限: {has_any}")

字典与集合的比较

python
# 字典与集合的比较

# 1. 创建方式
my_dict = {"a": 1, "b": 2}  # 字典:键值对
my_set = {1, 2, 3}          # 集合:只有值

# 2. 空对象的创建
empty_dict = {}             # 空字典
empty_set = set()           # 空集合(注意:{} 是空字典)

print(f"空字典类型: {type(empty_dict)}")  # <class 'dict'>
print(f"空集合类型: {type(empty_set)}")   # <class 'set'>

# 3. 访问方式
my_dict = {"name": "张三", "age": 20}
my_set = {1, 2, 3}

print(f"字典访问: {my_dict['name']}")     # 通过键访问
# print(my_set[0])  # 集合不能通过索引访问

# 4. 可变性
# 字典和集合都是可变的
my_dict["city"] = "北京"
my_set.add(4)
print(f"修改后字典: {my_dict}")
print(f"修改后集合: {my_set}")

# 5. 性能特点
# 字典和集合的查找时间复杂度都是 O(1)
# 列表的查找时间复杂度是 O(n)

import time

# 测试查找性能
large_list = list(range(100000))
large_set = set(range(100000))
large_dict = {i: i for i in range(100000)}

# 列表查找
start = time.time()
99999 in large_list
list_time = time.time() - start

# 集合查找
start = time.time()
99999 in large_set
set_time = time.time() - start

# 字典查找
start = time.time()
99999 in large_dict
dict_time = time.time() - start

print(f"\n查找性能比较:")
print(f"列表查找时间: {list_time:.6f}秒")
print(f"集合查找时间: {set_time:.6f}秒")
print(f"字典查找时间: {dict_time:.6f}秒")

使用场景总结

数据结构特点适用场景
字典键值对,快速查找存储配置信息、计数器、映射关系
集合无序不重复,快速判断存在去重、集合运算、权限验证
python
# 实际应用示例

# 1. 使用字典作为计数器
text = "hello world"
counter = {}
for char in text:
    counter[char] = counter.get(char, 0) + 1
print(f"字符计数: {counter}")

# 使用 collections.Counter 更简洁
from collections import Counter
counter = Counter(text)
print(f"Counter结果: {counter}")

# 2. 使用字典存储配置
config = {
    "database": {
        "host": "localhost",
        "port": 3306,
        "name": "mydb"
    },
    "app": {
        "debug": True,
        "port": 8080
    }
}
print(f"数据库配置: {config['database']}")

# 3. 使用集合进行数据清洗
raw_data = ["张三", "李四", "张三", "王五", "李四", "赵六"]
clean_data = list(set(raw_data))
print(f"清洗后数据: {clean_data}")

# 4. 使用集合进行标签匹配
article_tags = {"Python", "编程", "教程"}
user_interests = {"Python", "Java", "前端"}

# 找到用户感兴趣的文章
matching_tags = article_tags & user_interests
print(f"匹配的标签: {matching_tags}")

# 判断文章是否与用户兴趣相关
is_relevant = bool(matching_tags)
print(f"文章是否相关: {is_relevant}")

小结

本章学习了Python中两种重要的数据结构:

字典(Dictionary)

  • 使用键值对存储数据
  • 键必须不可变且唯一
  • 支持快速查找、添加、删除
  • 适合存储结构化数据

集合(Set)

  • 存储不重复的元素
  • 元素必须不可变
  • 支持数学集合运算
  • 适合去重和成员判断

掌握字典和集合的使用,将帮助你更高效地处理各种数据处理任务。