目录
一、集合
1. 集合概述与特点
2. 集合定义与嵌套
3. 集合的增删改查
3.1 增
3.2 删
3.3 改
3.4 查
4. 数学运算
5. 循环遍历
二、字典
1. 字典概述与特点
2. 字典的定义与嵌套
3. 字典的增删改查
3.1 增/改
3.2 删
3.3 查
4. 循环遍历
三、数据容器通用操作
1. 统计与度量函数
2. 排序与逆序函数
3. 结构转换与组合
4. 逻辑判断函数
四、数据容器对比
1. 五种容器特性概述
2. 数据容器对比
五、综合案例
1. 案例一:文本单词频次统计
2. 案例二:数据去重与集合运算
总结
一、集合
1. 集合概述与特点
在 Python 中,集合(Set)是一种无序且无重复元素的集合数据结构。与列表和元组等序列类型不同,集合不记录元素的插入顺序,也不支持通过索引访问特定的元素
集合的核心特点包括:
确定性:集合中的元素必须是明确的
唯一性:集合内不允许存在重复的元素。若向集合中添加已存在的元素,集合不会发生变化。该特性使集合成为数据去重的高效工具
无序性:集合中的元素没有固定的顺序,遍历集合时输出的元素顺序可能与构建时不同
元素不可变性:集合是可变容器,但其中的元素必须是不可变类型(即可哈希对象),如整数、浮点数、字符串或元组等。因此,列表、字典或其他集合本身不能作为普通集合的元素
2. 集合定义与嵌套
在 Python 中,可以通过两种主要方式定义集合:使用大括号 {} 或内置函数 set()
使用大括号定义: 直接将多个元素用逗号隔开并包裹在大括号内
# 定义一个包含整数的集合 numbers = {1, 2, 3, 4, 5}使用 set 函数定义: 可以通过 set() 将其他可迭代对象(如列表、元组、字符串)转换为集合
# 从列表创建集合 char_set = set(['a', 'b', 'c']) # 结果为 {'a', 'b', 'c'}空集合的定义: 定义空集合必须使用 set(),而不能使用 {},因为 {} 在Python中默认用于定义空字典
empty_set = set() # 正确的空集合定义
集合的嵌套
由于集合的元素必须是可哈希(不可变)的,因此普通的 set 不能嵌套自身(即集合的元素不能是另一个 set)
如果需要实现集合的嵌套,必须使用 Python 提供的不可变集合类型——frozenset(冰冻集合)。frozenset 一经创建便无法修改,因此它是可哈希的,可以作为普通集合的元素
# 正确的集合嵌套示例 nested_set = {frozenset({1, 2}), frozenset({3, 4})}3. 集合的增删改查
集合不支持索引,因此其 "改" 与 "查" 的操作逻辑与序列类型有所不同。以下是集合的操作方法:
3.1 增
add(element):向集合中添加单个元素。如果元素已存在,则不进行任何操作
s = {1, 2} s.add(3) # s 变为 {1, 2, 3}update(iterable):用于一次性添加多个元素,参数必须是可迭代对象(如列表、元组或其他集合)
s = {1, 2} s.update([3, 4], {5, 6}) # s 变为 {1, 2, 3, 4, 5, 6}3.2 删
remove(element):移除指定元素。若元素不存在,会触发 KeyError 异常
s = {1, 2, 3} s.remove(2) # s 变为 {1, 3}discard(element):移除指定元素。若元素不存在,程序不会报错,操作静默完成
s = {1, 2, 3} s.discard(4) # 不报错,s 仍为 {1, 2, 3}pop():随机移除并返回集合中的一个元素。由于集合无序,无法预测被移除的元素。若集合为空,会触发 KeyError
clear():清空集合中的所有元素,使其成为空集合
3.3 改
注意:集合没有下标,也不支持 replace 方法,所以集合没有专门用于 "改" 的方法,但可以使用:remove + add 的组合,来达到 "修改" 的效果
# 将 20 改为 66 s1 = {10, 20, 30, 40, 50} s1.remove(20) s1.add(66)3.4 查
集合无法通过索引查询特定位置的元素,但可以通过 in 和 not in 关键字检测某个元素是否存在于集合中。由于集合基于哈希表实现,其查找的时间复杂度为 O(1),效率远高于列表
s = {1, 2, 3} print(2 in s) # 输出: True print(4 not in s) # 输出: True4. 数学运算
Python 集合完整支持数学意义上的集合论运算,包括交集、并集、差集和对称差集。这些运算可以通过运算符或对应的方法实现
| 运算类型 | 运算符 | 对应方法 | 说明 |
|---|---|---|---|
| 并集 | a | b | a.union(b) | 返回包含两集合所有元素的新集合 |
| 交集 | a & b | a.intersection(b) | 返回同时存在于两集合中的元素的新集合 |
| 差集 | a - b | a.difference(b) | 返回存在于 a 但不存在于 b 中的元素的新集合 |
| 对称差集 | a ^ b | a.symmetric_difference(b) | 返回两集合非共有元素组成的新集合 |
此外,还可以通过以下方法判断集合间的关系:
issubset():判断当前集合是否为另一个集合的子集
issuperset():判断当前集合是否为另一个集合的超集
isdisjoint():判断两个集合是否没有交集
5. 循环遍历
虽然集合是无序的,但它属于可迭代对象,可以使用 for 循环遍历其中的每一个元素。需要注意的是,遍历输出的顺序不代表任何特定的规律
fruits = {"apple", "banana", "cherry"} for fruit in fruits: print(fruit)二、字典
1. 字典概述与特点
在Python中,字典(Dictionary)是一种映射类型的非序列容器。它以键值对的形式存储数据,其中 "键" 与 "值" 之间存在一一对应的映射关系。字典常用于存储结构化数据以及需要通过特定标识符快速查找对应数据的场景
字典的核心特点包括:
键的唯一性与不可变性:字典中 "键"(Key)必须是唯一的,不允许存在重复的键。同时,键必须是不可变类型(可哈希对象),如整数、浮点数、字符串或元组。列表、字典或集合等可变对象不能作为字典的键
值的任意性:字典中的 "值"(Value)可以是任意数据类型,包括动态对象、列表、甚至另一个字典。值不需要唯一,多个不同的键可以映射到相同的值
有序性:在早期版本的 Python 中,字典是无序的。但自 Python 3.7 起,字典正式确保持有元素的插入顺序。遍历字典时,将按照键值对被添加的先后顺序输出
高效查找:字典内部基于哈希表实现。与通过遍历匹配元素的列表不同,字典通过键直接定位值,其查找、插入和删除操作的平均时间复杂度均为 O(1)
2. 字典的定义与嵌套
定义字典主要有使用大括号和使用内置函数 dict() 两种方式
使用大括号定义: 每个键值对使用冒号分隔,多个键值对之间用逗号
,隔开,整体包裹在大括号内# 定义一个存储用户信息的字典 user_info = {"name": "Alice", "age": 25, "is_admin": False}使用 dict 函数定义: 可以通过传递关键字参数,或传入包含双元组的可迭代对象来创建字典
# 方式一:关键字参数 d1 = dict(name="Bob", age=30) # 方式二:双元组列表 d2 = dict([("name", "Bob"), ("age", 30)]) print(d1) print(d2)空字典的定义: 直接使用空的大括号或不带参数的 dict() 函数
empty_dict_1 = {} empty_dict_2 = dict()
字典嵌套
由于字典的值可以接收任意数据类型,因此字典可以实现多层嵌套。字典嵌套常用于表示复杂的层级结构,例如 JSON 数据
# 字典嵌套示例:存储多名学生的信息 students = { "1001": {"name": "张三", "scores": {"math": 95, "english": 88}}, "1002": {"name": "李四", "scores": {"math": 92, "english": 90}} }3. 字典的增删改查
对字典的操作主要是围绕键值对进行的。由于字典通过键进行索引,其操作逻辑高度依赖于键的存在性。
3.1 增/改
在字典中,增加和修改元素使用相同的语法。通过 dict[key] = value 进行赋值时:
若对应的键不存在,则会在字典中追加该键值对(增)
若对应的键已存在,则会覆盖原有的值(改)
info = {"name": "Alex"} # 键不存在,执行添加操作 info["age"] = 18 # info 变为 {"name": "Alex", "age": 18} print(info) # 键已存在,执行修改操作 info["name"] = "Alexander" # info 变为 {"name": "Alexander", "age": 18} print(info)update([other]):使用另一个字典或可迭代的键值对来更新当前字典。若键存在则覆盖,不存在则添加
info.update({"age": 20, "gender": "male"})3.2 删
del dict[key]:删除指定的键值对。若键不存在,会触发 KeyError 异常
pop(key[, default]):移除指定键并返回对应的值。若键不存在,且未提供 default 参数,会触发 KeyError;若提供了 default,则返回该默认值
age = info.pop("age", None) # 安全删除,若不存在则返回 Nonepopitem():移除并返回字典中的最后一个键值对。返回的结果是一个 (key, value) 形式的元组
clear():清空字典中的所有键值对
3.3 查
下标访问 dict[key]:直接通过键获取值。如果键不存在,会直接抛出 KeyError
get(key[, default]):安全的访问方法。若键存在,返回对应的值;若键不存在,则返回 default 参数指定的值(默认为 None),而不会引发程序中断
# 推荐的访问方式 gender = info.get("gender", "unknown") print(gender)4. 循环遍历
字典提供了三种视图对象(View Objects),分别用于遍历键、值或键值对。这些对象会随着字典的改变进行动态更新
遍历键(Keys): 直接遍历字典对象,或调用 keys() 方法
# 默认遍历的就是键 for key in info: print(key)
遍历值(Values): 调用 values() 方法
for value in info.values(): print(value)遍历键值对(Items): 调用 items() 方法,通常配合元组拆包使用
for key, value in info.items(): print(f"Key: {key}, Value: {value}")
三、数据容器通用操作
在 Python 中,有许多内置的全局函数并不局限于特定的容器类型,而是适用于几乎所有的数据容器(包括列表、元组、字符串、集合与字典)。将这些通用操作归纳在此处,有助于理解 Python 的数据结构体系
1. 统计与度量函数
len(iterable):
返回容器中元素的个数(对于字典,返回的是键值对的数量)。所有标准容器在底层都实现了 __len__ 方法,因此该操作的时间复杂度为 O(1)
len([1, 2, 3]) # 列表: 3 len({"a": 1, "b": 2}) # 字典: 2max(iterable) / min(iterable):
分别返回容器中的最大值和最小值
如果容器是字典,默认对键(Keys)进行比较
可以通过 key 参数传入一个函数,以此自定义比较的规则
若容器为空且未提供 default 参数,会触发 ValueError
max({1, 5, 3}) # 集合: 5 max({"a": 10, "b": 5}) # 字典(按键比较): "b" max({"a": 10, "b": 5}.values()) # 字典(按值比较): 10sum(iterable):
计算容器中所有元素的累加和。容器中的元素必须是数值类型(如整数、浮点数),否则会触发 TypeError
sum({1, 2, 3}) # 集合: 6
2. 排序与逆序函数
sorted(iterable):
对容器中的元素进行排序。无论输入的容器是什么类型,sorted() 都会返回一个全新的列表,原容器的内部结构和顺序不会发生改变
key 参数用于指定一个函数,在每个元素比较前先进行调用
reverse=True 表示降序排列
s = {3, 1, 4, 2} sorted_list = sorted(s) # 返回列表: [1, 2, 3, 4],原集合 s 不变 d = {"c": 1, "a": 3, "b": 2} sorted(d) # 对字典的键排序,返回: ['a', 'b', 'c']reversed(seq):
返回一个反向迭代器。注意:该函数仅适用于序列类型(如列表、元组、字符串)和确定具有顺序的容器。由于普通集合是无序的,因此对集合执行 reversed() 会触发 TypeError
3. 结构转换与组合
enumerate(iterable):将一个可迭代对象组合为一个索引序列,每次迭代时返回一个包含 (index, element) 的元组。常用于在 for 循环中同时获取索引和元素值
for index, value in enumerate(["a", "b", "c"]): print(index, value) # 输出: 0 a, 1 b, 2 czip(*iterables):将多个可迭代对象打包成元组,然后返回一个由这些元组组成的迭代器
keys = ["name", "age"] values = ["Alice", 25] # 结合 dict() 可以直接将两个序列转换为字典 result = dict(zip(keys, values)) # {'name': 'Alice', 'age': 25}
4. 逻辑判断函数
any(iterable):
判断容器中是否至少有一个元素的布尔值为 True。如果容器中有一个元素为真,则返回 True;如果容器为空或所有元素都为假,则返回 False
any([0, False, 1]) # 返回 True any([]) # 空容器返回 Falseall(iterable):
判断容器中是否所有元素的布尔值都为 True。只有当容器内所有元素均为真时,才返回 True如果容器为空,all() 的返回值是 True(在逻辑学中被称为 "空真理",Vacuous Truth)
all([1, True, "hello"]) # 返回 True all([1, 0, True]) # 返回 False(因为包含0) all([]) # 空容器返回 True
四、数据容器对比
为了在实际开发中能够准确、高效地选择合适的数据容器,我们需要对 Python 的五种核心数据容器(List、Tuple、String、Set、Dict)在技术特性上进行对比
1. 五种容器特性概述
列表(List):
一种可变的、有序的无约束序列。支持任意类型对象的存储,支持重复元素,通过索引进行 O(1) 的定位,但在执行包含性查找(如 in 操作)或在非末尾位置插入/删除元素时,时间复杂度为 O(n)
元组(Tuple):
一种不可变的、有序的无约束序列。其特性与列表高度相似,但由于其不可变性,元组通常用于表示结构化的常量数据。此外,元组可以用作字典的键或集合的元素(前提是元组内部不包含可变对象)
字符串(String):
一种不可变的、有序的单一类型序列。字符串专门用于存储文本字符。由于字符在内存中是连续存储的,且具有不可变性,对其进行修改操作实质上都会创建全新的字符串对象
集合(Set):
一种可变的、无序的唯一性非序列容器。集合通过哈希表实现,元素必须是不可变(可哈希)类型。集合天然具备去重功能,并且其元素的查找、添加、删除操作在平均情况下的时间复杂度均为 O(1)
字典(Dict):
一种可变的、有序的(Python 3.7+ 保持插入顺序)键值对映射容器。字典的键必须唯一且不可变,而值可以为任意类型。它同样基于哈希表实现,能够通过键以 O(1) 的时间复杂度快速检索对应的值
2. 数据容器对比
| 类型 | 是否有序 | 是否可变 | 唯一性 | 类型约束 | 底层实现 | 常见应用场景 | 查找复杂度 |
List | 是 | 是 | 允许重复 | 无约束,可存储任意类型 | 动态数组 | 存储、遍历和动态修改一组同类数据 | O(n) |
Tuple | 是 | 否 | 允许重复 | 无约束,可存储任意类型 | 静态数组 | 配置文件、函数多返回值、作为字典的键 | O(n) |
String | 是 | 否 | 允许重复 | 仅能存储字符 | 连续字符内存块 | 文本处理、数据传输与展示 | O(n) |
Set | 否 | 是 | 必须唯一 | 必须是不可变类型 | 哈希表 | 数据去重、数学集合运算 | O(1) |
Dict | 是 | 是 | 键唯一 (值可重复) | 键必须不可变; 值无约束 | 哈希表 | 存储结构化数据、需要通过Key快速检索的场景 | O(1) |
五、综合案例
为了更好地理解非序列容器(集合与字典)在实际开发中的应用,本节将通过两个典型案例,展示如何利用它们的特性解决具体问题
1. 案例一:文本单词频次统计
场景描述:给定一段英文文本,需要统计该文本中每个单词出现的次数,并输出频次最高的前三个单词
技术分析:此案例需要建立 "单词 -> 频次" 的映射关系,最适合使用字典。同时,由于要对频次进行降序排列,需结合第三部分介绍的 sorted() 通用函数
# 原始文本数据 text = "python is powerful python is open source python data science is growing" # 1. 将文本按空格切分为单词列表 words = text.split() # 2. 统计词频 word_counts = {} for word in words: # 使用 get() 方法处理键不存在的情况,若不存在则初始记为 0,然后加 1 word_counts[word] = word_counts.get(word, 0) + 1 print("完整统计结果:", word_counts) # 3. 按词频(Dict 的 Value)进行降序排序并取前三名 # 利用 sorted 的 key 参数指定按字典的值进行比较 top_three = sorted(word_counts.items(), key=lambda item: item[1], reverse=True)[:3] print("\n词频最高的前三个单词:") for word, count in top_three: print(f"单词: '{word}', 出现次数: {count}")2. 案例二:数据去重与集合运算
场景描述:现有两个线上活动的用户抽奖参与记录(允许单人多次提交,生成了包含重复用户ID的列表)。需求为:
分别获取两个活动的不重复参与用户总数
找出同时参与了这两个活动的活跃用户。技术分析:利用集合的唯一性可以完成数据去重;利用集合的交集运算可以高效筛选出同时满足两个条件的数据
# 活动 A 和活动 B 的原始参与记录(包含大量重复 ID) activity_a_records = ["user_101", "user_102", "user_101", "user_103", "user_102"] activity_b_records = ["user_103", "user_104", "user_105", "user_103", "user_101"] # 1. 利用集合自动去重 unique_users_a = set(activity_a_records) unique_users_b = set(activity_b_records) print(f"活动 A 独立参与人数: {len(unique_users_a)} 人 (去重后: {unique_users_a})") print(f"活动 B 独立参与人数: {len(unique_users_b)} 人 (去重后: {unique_users_b})") # 2. 运用数学运算:求交集,找出同时参与两项活动的用户 core_users = unique_users_a & unique_users_b print(f"同时参与两项活动的核心用户: {core_users}") # 输出: {'user_101', 'user_103'}总结
本章继续学习了 Python 中另外两种重要的数据容器——集合和字典,并对 Python 五种核心数据容器进行了系统梳理。通过本章的学习,我们不仅掌握了集合和字典的创建、增删改查、遍历及常用方法,还了解了它们在去重、快速查找、键值映射等实际开发场景中的优势
除此之外,我们还学习了数据容器的一些通用操作,并通过综合案例进一步体会了不同数据容器在实际开发中的使用方式。需要注意的是,并不存在一种万能的数据容器,选择合适的数据结构往往比单纯掌握语法更加重要。只有理解每种数据容器的特点和适用场景,才能编写出更加简洁、高效、易维护的代码
至此,Python 最基础的五种数据容器——列表、元组、字符串、集合和字典已经全部学习完毕。在下一篇文章中,我们将开始学习类和对象等语法,进一步掌握如何利用 Python 编写更加简洁、优雅且高效的代码