在Python开发中,列表(list)、元组(tuple)、字典(dict)和集合(set)是最常用的可迭代对象。虽然它们都支持for...in循环,但由于底层数据结构不同(序列、映射、哈希表),遍历方式各有特点。本文从基础到深入,系统梳理遍历这些数据结构的常见方法,并重点剖析集合的底层原理与性能优势。
- 基础遍历模式:所有可迭代对象都适用for...in循环,但根据需求不同,有多种变体写法。
复制代码
一、遍历列表(List)与元组(Tuple)
列表和元组都是有序序列,元组不可变但遍历语法完全一致。- lst = ['a', 'b', 'c']
- # 1. 直接遍历元素(最常用)
- for item in lst:
- print(item)
- # 2. 通过索引遍历(range+长度)
- for i in range(len(lst)):
- print(f"索引{i}: {lst[i]}")
- # 3. 同时获取索引和元素(enumerate,Pythonic推荐)
- for index, value in enumerate(lst):
- print(f"第{index}个元素是{value}")
- # 可自定义起始序号
- for index, value in enumerate(lst, start=1):
- print(f"第{index}个元素是{value}")
- # 4. 反向遍历(reversed,不修改原列表)
- for item in reversed(lst):
- print(item)
- # 5. 并行遍历多个序列(zip)
- names = ['A', 'B']
- scores = [90, 85]
- for name, score in zip(names, scores):
- print(f"{name}: {score}")
- # 6. 使用while循环(通用,但不推荐单纯遍历)
- i = 0
- while i < len(lst):
- print(lst[i])
- i += 1
复制代码
二、遍历字典(Dict)
字典是键值对映射,Python 3.7+保证插入顺序。- d = {'name': 'Tom', 'age': 18}
- # 1. 默认遍历键(等效于d.keys())
- for key in d:
- print(key)
- # 2. 遍历值
- for value in d.values():
- print(value)
- # 3. 同时遍历键和值(最常用)
- for key, value in d.items():
- print(f"{key} -> {value}")
- # 4. 遍历时修改字典(需转为列表避免RuntimeError)
- # 错误:for k in d: del d[k] # RuntimeError
- # 正确做法:
- for k in list(d.keys()):
- if k == 'age':
- del d[k]
复制代码
三、遍历集合(Set)——基础篇
集合是无序、元素唯一的哈希表,遍历时不保证输出顺序。- s = {1, 2, 3, 'a'}
- # 1. 标准for循环
- for item in s:
- print(item) # 顺序可能每次不同
- # 2. 使用enumerate获取人为序号(仅表示遍历次序)
- for idx, item in enumerate(s):
- print(f"第{idx}个取出的元素是: {item}")
- # 3. 使用迭代器(iter和next)手动控制
- it = iter(s)
- while True:
- try:
- item = next(it)
- print(item)
- except StopIteration:
- break
- # 4. 集合推导式(遍历并生成新集合)
- new_set = {x*2 for x in s if isinstance(x, int)}
复制代码 注意:集合不支持索引(s[0])和切片,无法通过range(len(s))按位置遍历。
四、集合深度详解
集合基于哈希表实现,具有以下核心特性:
- 无序性:元素存放位置由哈希值决定,CPython 3.6+虽保留插入顺序但官方不保证,不应依赖。
- 唯一性:自动去重,两个相等的元素(==)只保留一个。
- 元素必须可哈希:只能放入不可变类型(int, str, tuple),放入list/dict/set会报TypeError: unhashable type。
1. 创建集合的陷阱- a = {} # 这是空字典,不是集合!
- b = set() # 正确的空集合
- c = {1, 2, 3} # 非空集合
复制代码
2. 核心增删改查方法- s = {1, 2, 3}
- s.add(4) # 添加元素,已存在无效果
- s.update([5,6]) # 批量添加(合并另一个可迭代对象)
- s.remove(3) # 删除元素,不存在则KeyError
- s.discard(10) # 删除元素,不存在不报错(推荐使用)
- s.pop() # 随机弹出一个元素,空集合调用报错
- s.clear() # 清空集合
复制代码
3. 集合数学运算(高频考点,性能极高)- A = {1, 2, 3}
- B = {3, 4, 5}
- print(A | B) # {1, 2, 3, 4, 5} 并集,相当于A.union(B)
- print(A & B) # {3} 交集,相当于A.intersection(B)
- print(A - B) # {1, 2} 差集,相当于A.difference(B)
- print(A ^ B) # {1, 2, 4, 5} 对称差集,相当于A.symmetric_difference(B)
- print(A <= B) # False 子集判断,相当于A.issubset(B)
- print(A.isdisjoint(B)) # False 判断是否不相交
复制代码
4. 巨大的性能优势——成员检测
列表的if x in list时间复杂度O(n)(遍历查找),而集合的if x in set是O(1)(哈希直接命中)。实战中,若需频繁判断元素是否存在,应先将列表转为集合再操作。
5. 不可变集合——frozenset
业务中可能需要将集合作为字典的键或另一个集合的元素,但普通集合可变不可哈希。frozenset是集合的不可变版本,可哈希。- fs = frozenset([1, 2, 3])
- d = {fs: "value"} # 合法
复制代码
6. 遍历集合时修改集合(同样不安全)
遍历中增删集合大小会报错。解决方案:遍历其副本。- s = {1, 2, 3, 4}
- for item in list(s): # 转换为列表副本
- if item % 2 == 0:
- s.remove(item)
- print(s) # {1, 3}
复制代码
总结对比:列表和元组遍历方式一致,有序、允许重复、可通过索引访问;字典有序(3.7+)、键唯一值可重复、按键名获取值;集合无序不可依赖、自动去重、不可索引。Python之禅说“扁平比嵌套更好”,遍历时尽量用for...in直接解包,少用range(len()),代码更具可读性。 |