for 循环
Python 的 for 循环与 C/Java 中的 for 不同,它本质上是 遍历可迭代对象 的迭代器协议语法糖。无论遍历列表、字符串、字典,还是数字序列,for 都能优雅地处理。
for...in 基本语法
for 循环形式为 for 变量 in 可迭代对象:,每次取出一个元素赋给变量:
# 遍历列表
fruits = ["苹果", "香蕉", "橙子"]
for fruit in fruits:
print(fruit)
输出:
苹果
香蕉
橙子
# 遍历字符串
for char in "Python":
print(char, end=" ")
# 输出:P y t h o n
# 遍历字典
person = {"name": "小明", "age": 20, "city": "北京"}
for key in person:
print(f"{key}: {person[key]}")
:::tip 同时获取键和值
遍历字典时,使用 .items() 方法可以同时拿到键和值,比通过键查值更清晰:
person = {"name": "小明", "age": 20}
for key, value in person.items():
print(f"{key}: {value}")
:::
range() 函数
range() 用于生成整数序列,常用于按次数循环。它有三种形式:
# range(stop):0 到 stop-1
for i in range(5):
print(i, end=" ")
# 输出:0 1 2 3 4
print()
# range(start, stop):start 到 stop-1
for i in range(2, 6):
print(i, end=" ")
# 输出:2 3 4 5
print()
# range(start, stop, step):带步长
for i in range(0, 10, 2):
print(i, end=" ")
# 输出:0 2 4 6 8
print()
# 步长为负,倒序
for i in range(5, 0, -1):
print(i, end=" ")
# 输出:5 4 3 2 1
:::note range 是惰性序列
range() 返回的并非列表,而是 range 对象,它按需生成数字,不会一次性占用内存。因此 range(10_000_000) 几乎不消耗内存。需要列表时可用 list(range(5)) 转换。
:::
enumerate() 获取索引
需要同时获取索引和元素时,使用 enumerate():
fruits = ["苹果", "香蕉", "橙子"]
for index, fruit in enumerate(fruits):
print(f"{index}: {fruit}")
输出:
0: 苹果
1: 香蕉
2: 橙子
enumerate() 的第二个参数指定起始索引(默认 0):
for index, fruit in enumerate(fruits, start=1):
print(f"第 {index} 个:{fruit}")
:::warning 不要用 range(len(...)) 很多初学者会写出这样的代码:
fruits = ["苹果", "香蕉"]
for i in range(len(fruits)):
print(fruits[i])
这是 C 语言风格,在 Python 中不推荐。如果需要索引,请使用 enumerate();如果只需要元素,直接遍历即可。
:::
zip() 并行遍历
zip() 把多个可迭代对象"拉链式"配对:
names = ["小明", "小红", "小刚"]
ages = [20, 21, 19]
cities = ["北京", "上海", "广州"]
for name, age, city in zip(names, ages, cities):
print(f"{name},{age} 岁,来自{city}")
输出:
小明,20 岁,来自北京
小红,21 岁,来自上海
小刚,19 岁,来自广州
:::note zip 的长度
zip() 以最短的可迭代对象为准。如果想让长的截断补默认值,可使用 itertools.zip_longest:
from itertools import zip_longest
for a, b in zip_longest([1, 2, 3], [10, 20], fillvalue=0):
print(a, b)
# 输出:
# 1 10
# 2 20
# 3 0
:::
Python 3.10+ 还可以用 zip(..., strict=True) 在长度不一致时抛出异常,避免静默截断造成的 bug:
list(zip([1, 2, 3], [10, 20], strict=True))
# ValueError: zip() argument 2 is shorter than argument 1
嵌套循环
for 循环内可以再嵌套 for 循环,常用于处理二维结构:
matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
]
for row in matrix:
for value in row:
print(value, end=" ")
print()
输出:
1 2 3
4 5 6
7 8 9
:::tip 用推导式简化嵌套 扁平化嵌套循环可以用列表推导式或生成器表达式。例如把二维矩阵展平为一维列表:
matrix = [[1, 2, 3], [4, 5, 6]]
flat = [value for row in matrix for value in row]
print(flat) # [1, 2, 3, 4, 5, 6]
:::
for...else 语句
for 循环可以带 else 子句,仅在循环 正常结束(没有被 break 打断) 时执行:
def find_even(numbers: list[int]) -> int | None:
"""返回第一个偶数,找不到返回 None。"""
for n in numbers:
if n % 2 == 0:
print(f"找到偶数:{n}")
return n
else:
# 循环正常结束,说明没找到
print("没有偶数。")
return None
find_even([1, 3, 5, 7, 8]) # 找到偶数:8
find_even([1, 3, 5]) # 没有偶数。
:::warning for...else 容易误用
for...else 是 Python 中较少见的语法,许多开发者甚至不知道它的存在。它的"正常结束"语义容易被误解为"没找到元素时执行"。在团队代码中,建议用更显式的方式(如设置标志变量或提前 return)替代,以提升可读性。
:::
循环变量作用域
与许多其他语言不同,Python 的 for 循环变量在循环结束后 仍然存在,保留最后一次迭代的值:
for i in range(3):
pass
print(i) # 2
:::warning 注意空可迭代对象
如果可迭代对象为空,循环体一次都不执行,循环变量 未定义,访问会抛出 NameError:
for x in []:
pass
print(x) # NameError: name 'x' is not defined
如果担心这种情况,应在循环前给变量一个默认值,或使用 for...else 处理空场景。
:::
此外,循环变量在函数闭包中也有陷阱(延迟绑定问题),需要注意:
# 常见陷阱:闭包延迟绑定
funcs = []
for i in range(3):
funcs.append(lambda: i)
for f in funcs:
print(f(), end=" ")
# 输出:2 2 2 (而非 0 1 2)
因为 lambda 引用的是变量 i 本身,循环结束后 i == 2。修正方法是绑定默认参数:
funcs = [lambda i=i: i for i in range(3)]
for f in funcs:
print(f(), end=" ")
# 输出:0 1 2
实战:九九乘法表
经典的九九乘法表演示了嵌套循环和格式化输出:
def multiplication_table() -> None:
"""打印九九乘法表。"""
for i in range(1, 10):
for j in range(1, i + 1):
# 使用 f-string 格式化,右对齐保持列宽一致
print(f"{j}×{i}={i * j:<2}", end=" ")
print() # 换行
multiplication_table()
输出:
1×1=1
1×2=2 2×2=4
1×3=3 2×3=6 3×3=9
1×4=4 2×4=8 3×4=12 4×4=16
1×5=5 2×5=10 3×5=15 4×5=20 5×5=25
1×6=6 2×6=12 3×6=18 4×6=24 5×6=30 6×6=36
1×7=7 2×7=14 3×7=21 4×7=28 5×7=35 6×7=42 7×7=49
1×8=8 2×8=16 3×8=24 4×8=32 5×8=40 6×8=48 7×8=56 8×8=64
1×9=9 2×9=18 3×9=27 4×9=36 5×9=45 6×9=54 7×9=63 8×9=72 9×9=81
:::tip f-string 对齐技巧
f"{x:<2}"表示左对齐,宽度为 2f"{x:>2}"表示右对齐f"{x:^4}"表示居中,宽度为 4
Python 3.12 还支持在 f-string 中嵌套引号、跨多行,让格式化更灵活。 :::
如果只想求和而不打印,可以用生成器表达式配合 sum(),无需显式循环:
# 计算 1 到 100 的和
total = sum(range(1, 101))
print(total) # 5050
# 九九乘法表所有乘积的和
total = sum(i * j for i in range(1, 10) for j in range(1, i + 1))
print(total) # 1155
小结
for循环遍历可迭代对象,是 Python 中最常用的循环形式。range()生成整数序列,常用于按次数循环,且是惰性的,不占内存。enumerate()同时获取索引和元素,比range(len(...))更 Pythonic。zip()并行遍历多个序列,strict=True可在长度不一致时报错。- 嵌套循环处理二维结构;列表推导式可简化简单嵌套。
for...else在循环正常结束时执行,但易被误用,慎用。- 循环变量在循环后仍可访问,且在闭包中存在延迟绑定陷阱。