Skip to content

Python 迭代:一行 for 循环走天下的通用法则

引言:点名册的多种拿法

开学第一天,老师要点名。名单可能写在不同的东西上:

  • 一张打印好的A4 纸(列表 list),有第 1 个、第 2 个……的顺序;
  • 一本字典式花名册(字典 dict),按学号排,但也有名字;
  • 一块白板,上面随手写了一串名字(字符串 str);
  • 甚至是一个抽签筒,每次只能抽一个,抽完才知道下一个是谁。

Python 的 for 循环就像一个万能点名器:不管名单写在什么上,只要能"一个一个拿出来",它就能挨个念。这种"挨个取元素"的动作,就是迭代(Iteration)


一、什么是迭代?

1.1 迭代的定义

如果给定一个 listtuple,我们可以通过 for 循环来遍历它,这种遍历就称为迭代

python
>>> names = ['Michael', 'Sarah', 'Tracy']
>>> for name in names:
...     print(name)
...
Michael
Sarah
Tracy

生活化理解:迭代就像传送带上的包裹——你站在终点,包裹一个一个传过来,你一个一个处理。你不需要知道总共有多少个,也不需要知道它们是按什么顺序放上来的,只要"来一个处理一个"就行。

1.2 和其他语言的对比:为什么 Python 更优雅

在 C 语言里,遍历数组必须靠下标

c
for (i = 0; i < length; i++) {
    n = list[i];
}

你得自己数着 0、1、2、3……还得时刻担心 i 会不会越界。

Python 把下标藏起来了,直接说"把每个元素给我":

python
for n in list:
    print(n)

生活化理解:C 语言像自己去书架上按编号取书;Python 像图书馆管理员把书一本本递到你手上。Python 的抽象程度更高,你只需关心"处理这本书",不用关心"这是第几号书架"。


二、不只是 list:所有可迭代对象都能 for

Python 的 for 循环不仅限于 list 和 tuple,只要是可迭代对象,无论有没有下标,都可以迭代

2.1 字典(dict):没有顺序也能迭代

python
>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> for key in d:
...     print(key)
...
a
b
c

注意:字典的存储不是按插入顺序排列的(在 Python 3.7+ 中其实按插入顺序,但逻辑上不要依赖),所以迭代结果的顺序可能和你想的不一样。

三种迭代方式

python
# 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 字符串:每个字符就是一个元素

python
>>> for ch in 'ABC':
...     print(ch)
...
A
B
C

字符串可以看成"字符组成的列表",自然也能迭代。

2.3 集合(set):无序但可迭代

python
>>> s = {1, 2, 3}
>>> for x in s:
...     print(x)

集合没有重复元素,也没有固定顺序,但照样能迭代。


三、怎么判断一个对象能否迭代?

方法是通过 collections.abc 模块的 Iterable 类型:

python
>>> 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 问题的出现

有时候我们不仅想要元素本身,还想要它的序号。比如:

python
names = ['A', 'B', 'C']
# 想要输出:
# 0 A
# 1 B
# 2 C

笨办法:自己维护一个计数器

python
i = 0
for name in names:
    print(i, name)
    i += 1

可以是可以,但多了一行初始化、一行自增,啰嗦。

4.2 enumerate 的解决方案

Python 内置的 enumerate 函数可以把一个列表变成索引-元素对

python
>>> for i, value in enumerate(['A', 'B', 'C']):
...     print(i, value)
...
0 A
1 B
2 C

一行搞定!生活化理解:就像老师点名时,既念名字又念学号——enumerate 自动把两样东西打包给你。

还可以指定起始序号:

python
>>> 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)

python
>>> 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 遍历文件行

python
with open('log.txt', 'r', encoding='utf-8') as f:
    for line in f:          # 文件对象也是可迭代的
        print(line.strip())

文件对象可以逐行迭代,不会一次性把整个文件读进内存,处理大文件时特别有用。

6.2 批量处理数据

python
scores = {'Alice': 85, 'Bob': 92, 'Charlie': 78}

# 找出最高分
for name, score in scores.items():
    if score == max(scores.values()):
        print(f'最高分:{name}{score} 分')

6.3 带条件的迭代

python
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。

python
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('测试成功!')

思路解析

  1. 先处理空列表的特殊情况;
  2. 假设第一个元素既是 min 也是 max;
  3. 迭代整个列表,每遇到更小的就更新 min,更大的就更新 max;
  4. 一轮迭代结束,两个值都找到了。

这就是迭代的威力:不需要提前知道列表有多长,一个一个看过去就行


八、常见误区与避坑指南

误区 1:在迭代过程中修改列表

python
# 危险写法
L = [1, 2, 3, 4, 5]
for x in L:
    if x % 2 == 0:
        L.remove(x)     # 边迭代边删除,会漏掉元素!
print(L)                 # 结果可能不是 [1, 3, 5]

原因:删除元素后,后面的元素会前移,但迭代器已经记住了"下一个位置",导致跳过某些元素。

正确做法:迭代副本,或构建新列表

python
# 方法 1:迭代副本
for x in L[:]:
    if x % 2 == 0:
        L.remove(x)

# 方法 2:构建新列表(推荐)
L = [x for x in L if x % 2 != 0]

误区 2:以为字典迭代有固定顺序

python
d = {'z': 1, 'a': 2, 'm': 3}
for k in d:
    print(k)    # 不要假设输出一定是 a, m, z

虽然 Python 3.7+ 中字典按插入顺序迭代,但逻辑上不要把程序的正确性建立在字典顺序上。如果需要排序,用 sorted(d)

误区 3:对不可迭代对象用 for

python
for x in 123:      # 报错!TypeError: 'int' object is not iterable
    print(x)

迭代前如果不确定对象类型,先用 isinstance(x, Iterable) 检查。

误区 4:enumerate 的索引起点记错

python
for i, v in enumerate(['a', 'b', 'c']):
    print(i, v)
# 输出是 0 a, 1 b, 2 c —— 默认从 0 开始,不是 1

如果需要从 1 开始,显式写 enumerate(..., start=1)

误区 5:混淆"迭代"和"索引"

python
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)),除非你真的需要下标。


九、动手练习

  1. 基础题:用迭代计算列表 [3, 1, 4, 1, 5, 9, 2, 6] 中所有偶数的和。
  2. 进阶题:用 enumerate 找出字符串 'hello world' 中所有字母 'o' 出现的位置(索引)。
  3. 挑战题:有两个列表 names = ['Alice', 'Bob', 'Charlie']scores = [85, 92, 78],用一次 for 循环(不用 zip)打印出每个人名和对应分数,格式如 Alice: 85
  4. 思考题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 dfor v in d.values()for k, v in d.items()
  • enumerate:同时获取索引和元素,告别手动计数器。
  • 核心心法:迭代是"来一个处理一个"的思维方式,把复杂遍历简化成统一的 for ... in,是 Python 简洁美学的重要体现。