for 循环
Python 的 for 循环与其他语言(如 C、Java)的算术递增循环有本质区别。它不在数值上逐步递增,而是在可迭代对象的元素上逐个迭代,按元素在序列中出现的顺序处理。这种设计让 for 循环天然适合遍历列表、字符串、字典等数据结构。
基本语法
for 语句的格式为 for 变量 in 可迭代对象:,后跟缩进的循环体:
words = ['cat', 'window', 'defenestrate']
for w in words:
print(w, len(w))
输出:
cat 3
window 6
defenestrate 12
循环变量 w 在每次迭代中被赋予序列中的下一个元素,直到所有元素处理完毕,循环自动结束。不需要像 while 那样手动维护计数器和终止条件。
迭代原理
for 循环的背后是迭代器协议。Python 首先调用可迭代对象的 __iter__() 方法获取一个迭代器,然后反复调用迭代器的 __next__() 获取下一个元素。当元素耗尽时,__next__() 抛出 StopIteration 异常,for 循环捕获该异常并优雅退出。
这意味着任何实现了迭代器协议的对象都可以被 for 循环遍历,包括列表、元组、字符串、字典、集合、文件对象、生成器等。
遍历序列
列表与元组
team = ["航仔", "翼王", "凌叔"]
for member in team:
print(f"团队成员:{member}")
字符串
字符串是可迭代对象,for 循环逐个字符处理:
text = "Python"
for ch in text:
print(ch.upper())
输出 P、Y、T、H、O、N 各占一行。
字典
遍历字典时,默认迭代的是键:
scores = {"航仔": 85, "翼王": 92, "凌叔": 78}
for name in scores:
print(f"{name}: {scores[name]}")
如果需要同时获取键和值,使用 items() 方法:
for name, score in scores.items():
print(f"{name}: {score}")
分别遍历键或值:
for name in scores.keys():
print(name)
for score in scores.values():
print(score)
集合
集合是无序的,for 循环按任意顺序遍历元素:
tags = {"python", "java", "go"}
for tag in tags:
print(tag)
迭代时修改多项集
在 for 循环中直接修改被遍历的多项集(如删除元素)很容易导致意外行为或错误,因为迭代器的状态与底层集合的变化不同步:
# 危险:直接删除会导致跳过某些元素
words = ['cat', 'window', 'defenestrate']
for w in words:
if len(w) > 6:
words.remove(w) # 不要这样做
Python 官方文档推荐两种安全策略:
策略一:迭代副本
users = {'Hans': 'active', 'Éléonore': 'inactive', '景太郎': 'active'}
for user, status in users.copy().items():
if status == 'inactive':
del users[user]
print(users)
users.copy().items() 创建了一个字典的浅拷贝,循环在副本上迭代,删除操作作用于原字典,两者互不干扰。
策略二:创建新多项集
active_users = {}
for user, status in users.items():
if status == 'active':
active_users[user] = status
这种"过滤并收集"的模式更安全、更高效,也是函数式编程中常见的做法。
for-else 结构
与 while 类似,for 循环也可以带 else 子句。else 在循环正常结束(即未被执行 break 中断)时运行:
for n in range(2, 10):
for x in range(2, n):
if n % x == 0:
print(f"{n} = {x} * {n//x}")
break
else:
print(f"{n} 是质数")
注意这里的 else 属于 for 循环而非内部的 if。当内层循环完整遍历所有可能的因数而未触发 break 时,else 子句执行,报告该数字是质数。for-else 的详细语义将在独立文档中讨论。
配合 range 与 enumerate
当需要固定次数的重复或同时获取索引时,for 常与 range() 和 enumerate() 配合使用:
# 循环 5 次
for i in range(5):
print(f"第 {i+1} 次")
# 同时获取索引和元素
fruits = ["apple", "banana", "cherry"]
for idx, fruit in enumerate(fruits, start=1):
print(f"{idx}. {fruit}")
enumerate() 返回一个迭代器,产生 (索引, 元素) 元组,默认索引从 0 开始,可通过 start 参数调整。
解包迭代
如果可迭代对象的每个元素都是序列(如元组列表),for 循环可以直接解包:
pairs = [(1, 'one'), (2, 'two'), (3, 'three')]
for num, word in pairs:
print(f"{num} 的英文是 {word}")
这种写法比先取元素再索引更简洁,也是遍历 dict.items() 的基础。
常见错误
修改循环变量不影响迭代:
for i in range(5):
print(i)
i += 10 # 无效,下次迭代 i 仍按序列取值
i += 10 确实修改了局部变量 i,但 for 循环在下一次迭代时会从 range 获取下一个值,覆盖该修改。
遍历空序列:
empty = []
for item in empty:
print("这不会执行")
else:
print("循环正常结束(零次迭代)")
空序列不会导致错误,循环体执行零次,else 子句(如果有)仍然会执行。
for 循环是 Python 中最常用的迭代工具,理解其迭代器原理和安全修改策略,能够避免许多隐蔽的 bug。