元组是"只读列表",集合是"不重复的数学集合",用对场景让代码更高效
🎯 本章目标学完本章,你会:
✅ 理解元组的特点和应用场景
✅ 掌握元组的基本操作和方法
✅ 理解集合的特点和数学运算
✅ 掌握集合的基本操作和集合运算
✅ 能在测试中选择合适的数据结构
🔐 元组(Tuple):不可变的序列
什么是元组?
元组是一种有序、不可变的数据结构。一旦创建,就不能修改、添加或删除元素。
现实比喻:
元组 = 身份证信息(姓名、身份证号、出生日期,不能改)
列表 = 购物清单(可以随时修改)
为什么需要元组?
数据安全:防止数据被意外修改
性能更好:比列表处理速度快
可作为字典键:因为不可变,所以可哈希
函数多返回值:函数返回多个值时,实际上是返回一个元组
创建元组
# 空元组empty_tuple = ()empty_tuple2 = tuple()# 一个元素的元组(注意必须有逗号!)single1 = (1,) # 正确:(1,)single2 = 1, # 正确:(1,)# single3 = (1) # 错误:这只是一个数字1,不是元组# 多个元素的元组fruits = ("苹果", "香蕉", "橙子")numbers = (1, 2, 3, 4, 5)# 不用括号也可以(元组打包)person = "张三", 25, "北京" # ("张三", 25, "北京")# 从列表创建list_data = [1, 2, 3]tuple_from_list = tuple(list_data) # (1, 2, 3)# 混合类型mixed = ("文本", 123, 3.14, True, None)# 测试中的元组test_case = ("TC001", "登录测试", "高", 5.2)status_codes = (200, 301, 400, 404, 500)
访问元组元素
# 索引访问(和列表一样)fruits = ("苹果", "香蕉", "橙子", "葡萄", "芒果")print(fruits[0]) # 苹果print(fruits[-1]) # 芒果print(fruits[2]) # 橙子# 切片操作print(fruits[1:4]) # ("香蕉", "橙子", "葡萄")print(fruits[:3]) # ("苹果", "香蕉", "橙子")print(fruits[::2]) # ("苹果", "橙子", "芒果")print(fruits[::-1]) # ("芒果", "葡萄", "橙子", "香蕉", "苹果")# 解包(Unpacking)person = ("张三", 25, "北京")name, age, city = personprint(name) # 张三print(age) # 25print(city) # 北京# 星号解包numbers = (1, 2, 3, 4, 5, 6, 7)first, *middle, last = numbersprint(first) # 1print(middle) # [2, 3, 4, 5, 6] # 注意:星号得到的是列表print(last) # 7# 实战:处理测试结果test_result = ("TC001", "登录测试", "passed", 2.5)test_id, test_name, status, duration = test_resultprint(f"测试{test_id}结果: {status}, 耗时{duration}秒")
元组的不可变性
# 元组创建后不能修改fruits = ("苹果", "香蕉", "橙子")# 以下操作都会报错!# fruits[0] = "芒果" # TypeError: 'tuple' object does not support item assignment# fruits.append("葡萄") # AttributeError: 'tuple' object has no attribute 'append'# fruits.remove("苹果") # AttributeError: 'tuple' object has no attribute 'remove'# del fruits[0] # TypeError: 'tuple' object doesn't support item deletion# 但是!如果元组包含可变对象,可以修改可变对象内部nested_tuple = (1, 2, [3, 4])nested_tuple[2].append(5) # 可以!修改列表内部print(nested_tuple) # (1, 2, [3, 4, 5])# 元组可以重新赋值fruits = ("苹果", "香蕉", "橙子")fruits = ("芒果", "西瓜") # 可以,创建新元组
元组的常用操作
fruits = ("苹果", "香蕉", "橙子", "葡萄", "香蕉")# 长度length = len(fruits) # 5# 计数count = fruits.count("香蕉") # 2count2 = fruits.count("苹果") # 1count3 = fruits.count("西瓜") # 0# 查找索引index = fruits.index("橙子") # 2index2 = fruits.index("香蕉") # 1(返回第一个)index3 = fruits.index("香蕉", 2) # 4(从索引2开始找)# 检查元素是否存在has_apple = "苹果" in fruits # Truehas_mango = "芒果" in fruits # Falsenot_has_mango = "芒果" not in fruits # True# 最大值/最小值(同类型可比较)numbers = (5, 2, 8, 1, 9)max_num = max(numbers) # 9min_num = min(numbers) # 1# 求和total = sum(numbers) # 25# 排序(返回列表)sorted_numbers = sorted(numbers) # [1, 2, 5, 8, 9]sorted_desc = sorted(numbers, reverse=True) # [9, 8, 5, 2, 1]# 连接元组tuple1 = (1, 2, 3)tuple2 = (4, 5, 6)combined = tuple1 + tuple2 # (1, 2, 3, 4, 5, 6)# 重复元组repeated = tuple1 * 3 # (1, 2, 3, 1, 2, 3, 1, 2, 3)
元组 vs 列表
特性 | 元组 | 列表 |
|---|
可变性 | ❌ 不可变 | ✅ 可变 |
性能 | ⚡ 更快 | 🐢 较慢 |
内存占用 | 💾 较小 | 💾 较大 |
安全性 | 🔒 高(数据安全) | ⚠️ 低 |
功能 | 📦 较少 | 🧰 丰富 |
使用场景 | 常量、配置、函数返回值 | 动态数据集合 |
可哈希性 | ✅ 可哈希(可作为字典键) | ❌ 不可哈希 |
元组在测试中的应用
# 1. 存储常量配置HTTP_METHODS = ("GET", "POST", "PUT", "DELETE", "PATCH")TEST_ENVIRONMENTS = ("development", "staging", "production")PRIORITY_LEVELS = ("blocker", "critical", "major", "minor", "trivial")# 2. 函数返回多个值def run_test(test_name): """执行测试并返回结果""" # 模拟测试执行 import random status = random.choice(["passed", "failed", "error"]) duration = random.uniform(0.5, 5.0) error_message = "" if status == "passed" else "测试失败" return test_name, status, duration, error_message # 返回元组# 接收多个返回值test_name, status, duration, error = run_test("登录测试")print(f"测试: {test_name}")print(f"状态: {status}")print(f"耗时: {duration:.2f}秒")if error: print(f"错误: {error}")# 3. 作为字典的键(因为元组不可变)test_coverage = { ("登录模块", "V1.0"): 85, ("支付模块", "V1.0"): 90, ("用户模块", "V1.1"): 78}print(f"登录模块覆盖率: {test_coverage[('登录模块', 'V1.0')]}%")# 4. 命名元组(更高级的用法)from collections import namedtuple# 定义测试用例的结构TestCase = namedtuple("TestCase", ["id", "name", "priority", "duration"])# 创建测试用例实例tc1 = TestCase("TC001", "登录测试", "高", 5.2)tc2 = TestCase("TC002", "注册测试", "中", 3.8)# 访问(像属性一样)print(f"{tc1.id}: {tc1.name}, 优先级: {tc1.priority}")# 也可以通过索引访问print(f"耗时: {tc1[3]}秒")
🎲 集合(Set):无序的唯一集合
什么是集合?
集合是一种无序、不重复的数据结构。集合中的元素必须是可哈希的(不可变类型)。
现实比喻:
集合 = 数学中的集合,或者一个没有重复元素的袋子
列表 = 有序队列,可以有重复
元组 = 固定队列,不能修改
为什么需要集合?
去重:自动去除重复元素
集合运算:交集、并集、差集等
快速成员检查:比列表、元组快得多
数学建模:适合需要集合运算的场景
创建集合
# 空集合(注意:不能用{},{}是空字典)empty_set = set()# 创建集合fruits = {"苹果", "香蕉", "橙子"}numbers = {1, 2, 3, 4, 5}# 从列表创建(去重)duplicate_list = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]unique_set = set(duplicate_list) # {1, 2, 3, 4}# 从字符串创建chars = set("hello") # {'h', 'e', 'l', 'o'} # 注意:只有一个'l'# 从元组创建tuple_data = (1, 2, 2, 3, 3, 3)set_from_tuple = set(tuple_data) # {1, 2, 3}# 集合推导式squares = {x**2 for x in range(5)} # {0, 1, 4, 9, 16}even_squares = {x**2 for x in range(10) if x % 2 == 0} # {0, 4, 16, 36, 64}
集合的基本操作
# 添加元素fruits = {"苹果", "香蕉", "橙子"}fruits.add("葡萄") # 添加单个元素fruits.update(["芒果", "西瓜"]) # 添加多个元素fruits.update({"菠萝", "桃子"}) # 添加另一个集合的元素# 移除元素fruits = {"苹果", "香蕉", "橙子", "葡萄", "芒果"}fruits.remove("香蕉") # 移除元素,如果元素不存在会报错fruits.discard("西瓜") # 移除元素,如果元素不存在不会报错removed = fruits.pop() # 随机移除一个元素并返回fruits.clear() # 清空集合# 实战:管理测试用例test_cases = {"登录测试", "注册测试", "支付测试"}test_cases.add("搜索测试") # 添加新测试用例test_cases.update(["购物车测试", "订单测试"]) # 添加多个test_cases.discard("已废弃的测试") # 安全移除,不存在也不会报错# 检查元素是否存在fruits = {"苹果", "香蕉", "橙子"}has_apple = "苹果" in fruits # Truehas_mango = "芒果" not in fruits # True# 遍历集合for fruit in fruits: print(fruit)
集合运算
# 定义两个集合A = {1, 2, 3, 4, 5}B = {4, 5, 6, 7, 8}# 并集(A ∪ B)union = A | B # {1, 2, 3, 4, 5, 6, 7, 8}union2 = A.union(B) # 同上# 交集(A ∩ B)intersection = A & B # {4, 5}intersection2 = A.intersection(B) # 同上# 差集(A - B)difference = A - B # {1, 2, 3}difference2 = A.difference(B) # 同上# 对称差集(A △ B)symmetric_diff = A ^ B # {1, 2, 3, 6, 7, 8}symmetric_diff2 = A.symmetric_difference(B) # 同上# 子集和超集C = {2, 3}is_subset = C <= A # True,C是A的子集is_superset = A >= C # True,A是C的超集is_proper_subset = C < A # True,C是A的真子集is_proper_superset = A > C # True,A是C的真超集# 实战:测试用例分析all_test_cases = {"T1", "T2", "T3", "T4", "T5", "T6"}executed_test_cases = {"T1", "T3", "T5"}passed_test_cases = {"T1", "T3"}# 未执行的测试用例not_executed = all_test_cases - executed_test_cases # {"T2", "T4", "T6"}# 执行但未通过的测试用例executed_but_failed = executed_test_cases - passed_test_cases # {"T5"}# 通过的测试用例占比if all_test_cases: pass_rate = len(passed_test_cases) / len(all_test_cases) * 100else: pass_rate = 0print(f"测试通过率: {pass_rate:.1f}%")
集合的常用方法
# 创建两个集合A = {1, 2, 3, 4, 5}B = {4, 5, 6, 7, 8}# 长度length = len(A) # 5# 复制C = A.copy() # {1, 2, 3, 4, 5}# 判断是否不相交is_disjoint = A.isdisjoint({6, 7, 8}) # True,没有共同元素is_disjoint2 = A.isdisjoint({4, 5, 6}) # False,有共同元素# 更新方法(原地修改)A = {1, 2, 3, 4, 5}B = {4, 5, 6, 7, 8}A.update(B) # A变成{1, 2, 3, 4, 5, 6, 7, 8}A = {1, 2, 3, 4, 5}A.intersection_update(B) # A变成{4, 5}A = {1, 2, 3, 4, 5}A.difference_update(B) # A变成{1, 2, 3}A = {1, 2, 3, 4, 5}A.symmetric_difference_update(B) # A变成{1, 2, 3, 6, 7, 8}
集合在测试中的应用
# 1. 测试用例去重test_cases = ["登录测试", "注册测试", "登录测试", "支付测试", "注册测试"]unique_test_cases = set(test_cases) # {'登录测试', '注册测试', '支付测试'}print(f"去重后测试用例数: {len(unique_test_cases)}")# 2. 检查测试覆盖required_features = {"登录", "注册", "支付", "搜索", "购物车", "订单"}tested_features = {"登录", "注册", "支付"}# 已覆盖的功能covered = required_features & tested_featuresprint(f"已覆盖功能: {covered}")# 未覆盖的功能not_covered = required_features - tested_featuresprint(f"未覆盖功能: {not_covered}")# 覆盖率if required_features: coverage_rate = len(covered) / len(required_features) * 100else: coverage_rate = 0print(f"功能覆盖率: {coverage_rate:.1f}%")# 3. 查找重复的Bug报告bug_reports = [ "Bug001: 登录失败", "Bug002: 支付超时", "Bug003: 页面卡顿", "Bug004: 登录失败", # 重复 "Bug005: 支付超时" # 重复]# 查找重复的Bugseen = set()duplicates = set()for bug in bug_reports: if bug in seen: duplicates.add(bug) else: seen.add(bug)print(f"重复的Bug报告: {duplicates}")# 4. 权限检查user_roles = {"admin", "editor", "viewer"}required_roles = {"admin", "editor"}# 检查用户是否有足够权限if user_roles & required_roles: print("用户有部分权限")else: print("用户无权限")# 检查用户是否具有所有所需权限if required_roles <= user_roles: # 子集判断 print("用户具有所有所需权限")else: missing = required_roles - user_roles print(f"缺少权限: {missing}")
集合 vs 列表 vs 元组
特性 | 列表 | 元组 | 集合 |
|---|
有序性 | ✅ 有序 | ✅ 有序 | ❌ 无序 |
可变性 | ✅ 可变 | ❌ 不可变 | ✅ 可变 |
重复元素 | ✅ 允许 | ✅ 允许 | ❌ 不允许 |
索引访问 | ✅ 支持 | ✅ 支持 | ❌ 不支持 |
查找速度 | 🐢 慢 | 🐢 慢 | ⚡ 快 |
内存占用 | 💾 较大 | 💾 较小 | 💾 较大 |
主要用途 | 动态数据集合 | 固定数据集合 | 唯一元素、集合运算 |
📊 四种数据结构总结对比
数据结构 | 可变性 | 有序性 | 重复元素 | 使用场景 |
|---|
列表 | ✅ 可变 | ✅ 有序 | ✅ 允许 | 动态数据集合,需要增删改查 |
元组 | ❌ 不可变 | ✅ 有序 | ✅ 允许 | 常量、配置、函数返回值、字典键 |
集合 | ✅ 可变 | ❌ 无序 | ❌ 不允许 | 去重、集合运算、快速成员检查 |
字典 | ✅ 可变 | ✅ 有序(3.7+) | ✅ 键唯一 | 键值对映射、结构化数据 |
📋 本章检查清单
[ ] 理解元组是不可变的、有序的序列
[ ] 掌握元组的创建、访问和解包
[ ] 理解元组的不可变性及其应用场景
[ ] 理解集合是无序的、不重复的集合
[ ] 掌握集合的创建、添加、删除操作
[ ] 掌握集合运算(并集、交集、差集、对称差集)
[ ] 能在测试中灵活使用元组和集合
[ ] 理解列表、元组、集合、字典的区别和适用场景
🎉 恭喜!元组和集合掌握完成
现在你已经学会了Python中四种重要的数据结构:
列表:有序、可变、可重复
字典:键值对、无序、键唯一
元组:有序、不可变、可重复
集合:无序、不重复、集合运算
关键收获:
✅ 元组:用于不可变数据,如配置、函数返回值
✅ 集合:用于去重和集合运算
✅ 选择合适的数据结构:根据需求选择最合适的结构
下一章预告:序列-基本的数据结构序列
准备好了吗?让我们继续前进!🚀
记住:不同的数据结构适用于不同的场景。在写代码时,多思考一下:"这个数据用什么结构存储最合适?"