Python 迭代:一行 for 循环走天下的通用法则
引言:点名册的多种拿法
开学第一天,老师要点名。名单可能写在不同的东西上:
- 一张打印好的A4 纸(列表 list),有第 1 个、第 2 个……的顺序;
- 一本字典式花名册(字典 dict),按学号排,但也有名字;
- 一块白板,上面随手写了一串名字(字符串 str);
- 甚至是一个抽签筒,每次只能抽一个,抽完才知道下一个是谁。
Python 的 for 循环就像一个万能点名器:不管名单写在什么上,只要能"一个一个拿出来",它就能挨个念。这种"挨个取元素"的动作,就是迭代(Iteration)。
一、什么是迭代?
1.1 迭代的定义
如果给定一个 list 或 tuple,我们可以通过 for 循环来遍历它,这种遍历就称为迭代。
>>> names = ['Michael', 'Sarah', 'Tracy']
>>> for name in names:
... print(name)
...
Michael
Sarah
Tracy生活化理解:迭代就像传送带上的包裹——你站在终点,包裹一个一个传过来,你一个一个处理。你不需要知道总共有多少个,也不需要知道它们是按什么顺序放上来的,只要"来一个处理一个"就行。
1.2 和其他语言的对比:为什么 Python 更优雅
在 C 语言里,遍历数组必须靠下标:
for (i = 0; i < length; i++) {
n = list[i];
}你得自己数着 0、1、2、3……还得时刻担心 i 会不会越界。
Python 把下标藏起来了,直接说"把每个元素给我":
for n in list:
print(n)生活化理解:C 语言像自己去书架上按编号取书;Python 像图书馆管理员把书一本本递到你手上。Python 的抽象程度更高,你只需关心"处理这本书",不用关心"这是第几号书架"。
二、不只是 list:所有可迭代对象都能 for
Python 的 for 循环不仅限于 list 和 tuple,只要是可迭代对象,无论有没有下标,都可以迭代。
2.1 字典(dict):没有顺序也能迭代
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
... print(key)
...
a
b
c注意:字典的存储不是按插入顺序排列的(在 Python 3.7+ 中其实按插入顺序,但逻辑上不要依赖),所以迭代结果的顺序可能和你想的不一样。
三种迭代方式:
# 1. 只迭代 key(默认)
for key in d:
print(key)
# 2. 只迭代 value
for value in d.values():
print(value)
# 3. 同时迭代 key 和 value
for k, v in d.items():
print(k, v)生活化理解:字典就像电话簿——你可以只翻人名(key),只翻号码(value),或者人名和号码一起看(items)。
2.2 字符串:每个字符就是一个元素
>>> for ch in 'ABC':
... print(ch)
...
A
B
C字符串可以看成"字符组成的列表",自然也能迭代。
2.3 集合(set):无序但可迭代
>>> s = {1, 2, 3}
>>> for x in s:
... print(x)集合没有重复元素,也没有固定顺序,但照样能迭代。
三、怎么判断一个对象能否迭代?
方法是通过 collections.abc 模块的 Iterable 类型:
>>> from collections.abc import Iterable
>>> isinstance('abc', Iterable) # str 是否可迭代
True
>>> isinstance([1, 2, 3], Iterable) # list 是否可迭代
True
>>> isinstance(123, Iterable) # 整数是否可迭代
False生活化理解:就像检查一个东西能不能放进传送带——盒子(list)、信封(str)、抽屉(dict)都可以,但一块石头(int)不行,因为它不是"由多个部分组成的"。
四、enumerate:既要元素,也要序号
4.1 问题的出现
有时候我们不仅想要元素本身,还想要它的序号。比如:
names = ['A', 'B', 'C']
# 想要输出:
# 0 A
# 1 B
# 2 C笨办法:自己维护一个计数器
i = 0
for name in names:
print(i, name)
i += 1可以是可以,但多了一行初始化、一行自增,啰嗦。
4.2 enumerate 的解决方案
Python 内置的 enumerate 函数可以把一个列表变成索引-元素对:
>>> for i, value in enumerate(['A', 'B', 'C']):
... print(i, value)
...
0 A
1 B
2 C一行搞定!生活化理解:就像老师点名时,既念名字又念学号——enumerate 自动把两样东西打包给你。
还可以指定起始序号:
>>> for i, value in enumerate(['A', 'B', 'C'], start=1):
... print(i, value)
...
1 A
2 B
3 C五、同时迭代多个变量:解包的魔法
上面的 for i, value in ... 里,我们同时引用了两个变量。这在 Python 里很常见,叫做解包(Unpacking)。
>>> for x, y in [(1, 1), (2, 4), (3, 9)]:
... print(x, y)
...
1 1
2 4
3 9每次循环,元组 (1, 1) 被拆成 x=1, y=1;下一轮 (2, 4) 拆成 x=2, y=4。
生活化理解:就像快递拆包——每个包裹里有两样东西,你一次性把两样都拿出来摆好。
六、实战应用场景
6.1 遍历文件行
with open('log.txt', 'r', encoding='utf-8') as f:
for line in f: # 文件对象也是可迭代的
print(line.strip())文件对象可以逐行迭代,不会一次性把整个文件读进内存,处理大文件时特别有用。
6.2 批量处理数据
scores = {'Alice': 85, 'Bob': 92, 'Charlie': 78}
# 找出最高分
for name, score in scores.items():
if score == max(scores.values()):
print(f'最高分:{name},{score} 分')6.3 带条件的迭代
numbers = [1, 3, 5, 7, 8, 9, 11]
# 找出第一个偶数
for i, n in enumerate(numbers):
if n % 2 == 0:
print(f'第 {i} 个位置的 {n} 是偶数')
break七、经典练习:查找最小值和最大值
题目:使用迭代查找一个 list 中的最小值和最大值,并返回一个 tuple。
def findMinAndMax(L):
if len(L) == 0:
return (None, None)
min_val = L[0]
max_val = L[0]
for x in L:
if x < min_val:
min_val = x
if x > max_val:
max_val = x
return (min_val, max_val)
# 测试
if findMinAndMax([]) != (None, None):
print('测试失败!')
elif findMinAndMax([7]) != (7, 7):
print('测试失败!')
elif findMinAndMax([7, 1]) != (1, 7):
print('测试失败!')
elif findMinAndMax([7, 1, 3, 9, 5]) != (1, 9):
print('测试失败!')
else:
print('测试成功!')思路解析:
- 先处理空列表的特殊情况;
- 假设第一个元素既是 min 也是 max;
- 迭代整个列表,每遇到更小的就更新 min,更大的就更新 max;
- 一轮迭代结束,两个值都找到了。
这就是迭代的威力:不需要提前知道列表有多长,一个一个看过去就行。
八、常见误区与避坑指南
误区 1:在迭代过程中修改列表
# 危险写法
L = [1, 2, 3, 4, 5]
for x in L:
if x % 2 == 0:
L.remove(x) # 边迭代边删除,会漏掉元素!
print(L) # 结果可能不是 [1, 3, 5]原因:删除元素后,后面的元素会前移,但迭代器已经记住了"下一个位置",导致跳过某些元素。
正确做法:迭代副本,或构建新列表
# 方法 1:迭代副本
for x in L[:]:
if x % 2 == 0:
L.remove(x)
# 方法 2:构建新列表(推荐)
L = [x for x in L if x % 2 != 0]误区 2:以为字典迭代有固定顺序
d = {'z': 1, 'a': 2, 'm': 3}
for k in d:
print(k) # 不要假设输出一定是 a, m, z虽然 Python 3.7+ 中字典按插入顺序迭代,但逻辑上不要把程序的正确性建立在字典顺序上。如果需要排序,用 sorted(d)。
误区 3:对不可迭代对象用 for
for x in 123: # 报错!TypeError: 'int' object is not iterable
print(x)迭代前如果不确定对象类型,先用 isinstance(x, Iterable) 检查。
误区 4:enumerate 的索引起点记错
for i, v in enumerate(['a', 'b', 'c']):
print(i, v)
# 输出是 0 a, 1 b, 2 c —— 默认从 0 开始,不是 1如果需要从 1 开始,显式写 enumerate(..., start=1)。
误区 5:混淆"迭代"和"索引"
L = ['a', 'b', 'c']
# 不需要索引时,不要这样写
for i in range(len(L)):
print(L[i])
# 应该直接迭代
for x in L:
print(x)Python 之禅:能用 for x in L 就别用 for i in range(len(L)),除非你真的需要下标。
九、动手练习
- 基础题:用迭代计算列表
[3, 1, 4, 1, 5, 9, 2, 6]中所有偶数的和。 - 进阶题:用
enumerate找出字符串'hello world'中所有字母'o'出现的位置(索引)。 - 挑战题:有两个列表
names = ['Alice', 'Bob', 'Charlie']和scores = [85, 92, 78],用一次 for 循环(不用zip)打印出每个人名和对应分数,格式如Alice: 85。 - 思考题:
for x in [1, 2, 3]和for x in (1, 2, 3)有什么区别?for x in '123'呢?它们背后的共同点是什么?
小结
- 迭代(Iteration):用
for ... in逐个取出元素的过程,适用于所有可迭代对象。 - Python 的 for 比 C 更抽象:不需要关心下标和长度,直接面对元素本身。
- 可迭代对象:list、tuple、dict、set、str、文件对象等都可以用
for迭代;用isinstance(x, Iterable)可判断。 - dict 的三种迭代:
for k in d、for v in d.values()、for k, v in d.items()。 - enumerate:同时获取索引和元素,告别手动计数器。
- 核心心法:迭代是"来一个处理一个"的思维方式,把复杂遍历简化成统一的
for ... in,是 Python 简洁美学的重要体现。