Python3 循环语句

在 Python 3 中,循环语句 (Loop Statements) 是控制程序流、实现重复执行逻辑的核心机制。与其他语言(如 C 或 Java)不同,Python 的 for 循环本质上是 “for-each” 循环,它直接遍历可迭代对象中的元素,而不是通过索引计数器来控制。

为了让你全面、深入地掌握 Python 3 的循环机制,本文将从底层原理、基础语法、高级技巧、标准库利器到性能优化进行全方位、详尽的解析。(本文篇幅较长,旨在提供一份可作为生产环境参考手册的深度指南)


一、 循环的底层基石:可迭代对象与迭代器

要真正掌握 Python 的循环,必须先理解两个核心概念:可迭代对象 (Iterable)迭代器 (Iterator)

  1. 可迭代对象 (Iterable):任何可以被 for 循环遍历的对象。在底层,它必须实现了 __iter__() 方法,该方法返回一个迭代器。常见的 Iterable 包括:列表、元组、字符串、字典、集合,以及 range() 的返回值。
  2. 迭代器 (Iterator):表示数据流的对象。它必须同时实现 __iter__()__next__() 方法。每次调用 __next__() 时,它返回下一个元素;当没有元素时,抛出 StopIteration 异常,从而通知 for 循环结束。

for 循环的底层执行过程
当你写下 for item in my_list: 时,Python 解释器在底层实际执行了以下操作:

纯文本
# 1. 获取迭代器
iterator = iter(my_list)  # 调用 my_list.__iter__()

while True:
    try:
        # 2. 获取下一个元素
        item = next(iterator)  # 调用 iterator.__next__()
        # 3. 执行循环体
        print(item)
    except StopIteration:
        # 4. 捕获异常,优雅退出循环
        break

理解这一点至关重要:它解释了为什么你不能对整数 (int) 使用 for 循环(因为整数不是 Iterable),也解释了为什么在遍历字典时,直接遍历得到的是键 (Keys)。


二、 for 循环详解

for 循环是 Python 中最常用的循环结构,用于遍历序列或其他可迭代对象。

1. 基础遍历

纯文本
# 遍历列表
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
    print(fruit)

# 遍历字符串
for char in "Python":
    print(char, end=" ")  # 输出: P y t h o n 

2. range() 函数的进阶用法

在 Python 3 中,range() 不再返回列表,而是返回一个惰性求值 (Lazy Evaluation)range 对象,它只占用极少的内存(无论范围多大),仅在需要时生成数字。
语法:range(start, stop, step)

纯文本
# 1. 单个参数:从 0 开始,到 stop-1 结束
for i in range(5):
    print(i)  # 0, 1, 2, 3, 4

# 2. 两个参数:指定 start 和 stop
for i in range(2, 6):
    print(i)  # 2, 3, 4, 5

# 3. 三个参数:指定步长 (step)
for i in range(0, 10, 2):
    print(i)  # 0, 2, 4, 6, 8 (偶数)

# 4. 负数步长:用于倒序遍历
for i in range(5, 0, -1):
    print(i)  # 5, 4, 3, 2, 1

3. 遍历字典 (Dictionary)

字典有三种视图,决定了 for 循环的遍历内容:

纯文本
user = {"name": "Alice", "age": 28, "city": "NY"}

# 默认遍历键 (等同于 user.keys())
for key in user:
    print(key)

# 遍历值
for value in user.values():
    print(value)

# 遍历键值对 (最常用,结合元组解包)
for key, value in user.items():
    print(f"{key}: {value}")

三、 while 循环详解

while 循环在条件为真时持续执行代码块。它适用于循环次数未知,仅依赖某个状态或条件来决定是否继续的场景。

纯文本
count = 0
while count < 3:
    print(f"Count is {count}")
    count += 1  # ⚠️ 必须更新条件变量,否则会导致无限循环 (Infinite Loop)

⚠️ while 循环的常见陷阱

  1. 忘记更新循环变量:导致死循环,耗尽 CPU 资源。
  2. 浮点数比较:由于精度问题,避免在 while 条件中使用 == 比较浮点数,应使用 math.isclose()< / >

四、 循环控制语句:break, continue, passelse

1. break:立即终止整个循环

跳出当前所在的最内层循环,继续执行循环之后的代码。

纯文本
for i in range(10):
    if i == 5:
        break  # 当 i 等于 5 时,直接跳出循环
    print(i)   # 输出: 0, 1, 2, 3, 4

2. continue:跳过本次循环的剩余部分

立即结束当前迭代,直接进入下一次循环的判断。

纯文本
for i in range(5):
    if i == 2:
        continue  # 跳过 i=2 的这次迭代
    print(i)      # 输出: 0, 1, 3, 4

3. pass:空操作占位符

当语法上需要一条语句,但逻辑上不需要执行任何操作时使用(常用于构建代码骨架)。

纯文本
for item in data:
    if item is None:
        pass  # TODO: 稍后处理空值
    else:
        process(item)

4. 🌟 Python 独有特性:循环的 else 子句

这是 Python 中极具特色且常被误解的特性。for...elsewhile...else 中的 else仅在循环正常完成(即没有被 break 语句打断)时才会执行。如果循环是因为 break 退出的,else 块将被跳过。

经典应用场景:搜索与验证

纯文本
# 场景:在列表中查找素数
numbers = [4, 6, 8, 9, 11]

for n in numbers:
    for i in range(2, n):
        if n % i == 0:
            print(f"{n} 不是素数")
            break  # 找到因子,打断内层循环
    else:
        # 只有当内层循环没有被 break 打断时(即没有找到任何因子),才会执行这里
        print(f"{n} 是素数!")

# 输出:
# 4 不是素数
# 6 不是素数
# 8 不是素数
# 9 不是素数
# 11 是素数!

💡 提示:可以将 for...else 理解为 “for…no break”。这种写法避免了使用额外的布尔标志变量(如 found = True),使代码更优雅。


五、 高级循环技巧与内置函数 🌟

在循环中配合使用特定的内置函数,可以彻底消除对索引的依赖,写出极其 Pythonic 的代码。

1. enumerate(): 同时获取索引和值

永远不要使用 for i in range(len(my_list)): 这种反模式。

纯文本
fruits = ["apple", "banana", "cherry"]

# ✅ 推荐做法:start 参数可指定索引起始值(默认为 0)
for index, fruit in enumerate(fruits, start=1):
    print(f"第 {index} 个水果是 {fruit}")

2. zip(): 并行遍历多个可迭代对象

将多个序列“拉链”式地组合在一起。

纯文本
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]

for name, score in zip(names, scores):
    print(f"{name} 的得分是 {score}")

# 🌟 Python 3.10+ 新特性:strict=True
# 如果传入的可迭代对象长度不一致,zip 默认会以最短的为准截断。
# 设置 strict=True 后,长度不一致会抛出 ValueError,有助于及早发现数据 Bug。
ids = [1, 2]
# zip(names, ids, strict=True)  # 会抛出 ValueError,因为长度 3 != 2

3. reversed()sorted(): 改变遍历顺序

  • reversed(seq): 返回一个反向迭代器,不修改原对象,且比 seq[::-1] 更节省内存(不创建新列表)。
  • sorted(seq): 返回一个新的已排序的列表。
纯文本
nums = [3, 1, 4, 1, 5]

# 倒序遍历
for n in reversed(nums):
    print(n)  # 5, 1, 4, 1, 3

# 排序后遍历
for n in sorted(nums):
    print(n)  # 1, 1, 3, 4, 5

# 倒序且排序遍历
for n in sorted(nums, reverse=True):
    print(n)  # 5, 4, 3, 1, 1

六、 推导式 (Comprehensions):循环的终极进化

推导式是 Python 最具标志性的语法糖。它允许你用一行简洁的代码,基于现有的可迭代对象构建新的列表、字典或集合。更重要的是,推导式在 C 语言层面进行了优化,执行速度通常比等效的 for 循环加 append() 快 20% 到 50%

1. 列表推导式 (List Comprehension)

纯文本
# 需求:生成 0-9 中偶数的平方列表

# ❌ 传统写法
squares = []
for x in range(10):
    if x % 2 == 0:
        squares.append(x**2)

# ✅ 推导式写法 (表达式 + for 子句 + 可选的 if 子句)
squares = [x**2 for x in range(10) if x % 2 == 0]

2. 字典与集合推导式

纯文本
# 字典推导式:快速反转键值对
original = {"a": 1, "b": 2, "c": 3}
reversed_dict = {v: k for k, v in original.items()}  # {1: 'a', 2: 'b', 3: 'c'}

# 集合推导式:自动去重
text = "hello"
unique_chars = {char for char in text}  # {'h', 'e', 'l', 'o'}

3. 嵌套推导式

可以将多层 for 循环压缩为一行,但需严格控制复杂度以保证可读性

纯文本
# 生成坐标矩阵 (x, y) 其中 x 和 y 都在 0-2 之间
coords = [(x, y) for x in range(3) for y in range(3)]
# 等价于:
# coords = []
# for x in range(3):
#     for y in range(3):
#         coords.append((x, y))

⚠️ 最佳实践:如果推导式超过两行,或者包含复杂的逻辑判断,请果断退回到普通的 for 循环。可读性永远高于炫技


七、 标准库利器:itertools 模块

当面对复杂的迭代需求时,Python 标准库中的 itertools 模块提供了用 C 语言编写的高性能工具,是处理循环的“瑞士军刀”。

纯文本
import itertools

# 1. itertools.count(start, step): 创建无限递增的计数器
# 常用于为没有索引的流数据添加序号
for i, val in zip(itertools.count(1), ["a", "b", "c"]):
    print(f"{i}: {val}")  # 1: a, 2: b, 3: c

# 2. itertools.cycle(iterable): 无限循环遍历一个序列
# 常用于轮询调度 (Round-Robin)
colors = itertools.cycle(["red", "green", "blue"])
for _ in range(5):
    print(next(colors))  # red, green, blue, red, green

# 3. itertools.chain(*iterables): 将多个可迭代对象无缝连接成一个
list1 = [1, 2]
list2 = [3, 4]
for item in itertools.chain(list1, list2):
    print(item)  # 1, 2, 3, 4 (比 list1 + list2 更省内存,因为不创建新列表)

# 4. itertools.product(*iterables, repeat=1): 计算笛卡尔积
# 完美替代深层嵌套的 for 循环
for x, y in itertools.product([1, 2], ['a', 'b']):
    print(f"({x}, {y})")  
    # (1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')

八、 性能分析与核心避坑指南 (Big-O 复杂度)

编写循环时,微小的习惯差异可能导致巨大的性能鸿沟。以下是必须遵守的最佳实践:

1. ❌ 绝对不要在循环中修改正在遍历的可变序列

在遍历列表或字典时直接添加或删除元素,会导致迭代器状态混乱,引发 RuntimeError 或静默的逻辑错误(跳过元素)。

纯文本
nums = [1, 2, 3, 4, 5]

# ❌ 错误:运行时会跳过元素或报错
for n in nums:
    if n % 2 == 0:
        nums.remove(n) 

# ✅ 正确做法 1:遍历副本
for n in nums[:]:  # nums[:] 创建了浅拷贝
    if n % 2 == 0:
        nums.remove(n)

# ✅ 正确做法 2 (更推荐):使用列表推导式重建
nums = [n for n in nums if n % 2 != 0]

2. ❌ 避免在循环内部进行 $O(N)$ 的昂贵操作

将时间复杂度高的操作移出循环体,或选择合适的数据结构。

纯文本
# ❌ 反模式:在循环头部插入,每次都是 O(N),总体 O(N²)
result = []
for i in range(10000):
    result.insert(0, i) 

# ✅ 优化 1:尾部追加,最后反转 (总体 O(N))
result = []
for i in range(10000):
    result.append(i)
result.reverse()

# ✅ 优化 2:使用 collections.deque (双端队列,头部插入为 O(1))
from collections import deque
result = deque()
for i in range(10000):
    result.appendleft(i)

3. ❌ 避免在循环中使用 += 拼接字符串

由于字符串是不可变的,每次 += 都会创建一个新的字符串对象并复制所有字符,导致 $O(N^2)$ 的时间复杂度。

纯文本
# ❌ 低效
text = ""
for word in words:
    text += word + " "

# ✅ 高效:使用 str.join() (O(N))
text = " ".join(words)

4. 💡 局部变量查找快于全局/内置变量

在极度追求性能的循环(如百万次迭代)中,将频繁调用的函数或属性绑定到局部变量,可以节省名称查找的开销。

纯文本
import math

# 较慢:每次循环都要在全局/内置命名空间中查找 'math.sqrt'
for i in range(1000000):
    x = math.sqrt(i)

# 较快:将函数引用绑定到局部变量
sqrt = math.sqrt
for i in range(1000000):
    x = sqrt(i)

九、 总结

Python 3 的循环语句不仅仅是重复执行代码的工具,它是一套与语言底层数据模型(迭代器协议)深度绑定的优雅机制。

  • 首选 for 循环:因为它直接表达“对集合中的每个元素执行操作”的意图,且避免了索引管理的繁琐与越界风险。
  • 善用高级内置函数enumeratezipreversed 能让你的循环代码摆脱索引,变得声明式且易读。
  • 拥抱推导式:在逻辑简单时,用推导式替代 for + append,获得更好的性能和更简洁的语法。
  • 掌握 for...else:在搜索和验证场景中,它是消除冗余标志变量的利器。
  • 警惕性能陷阱:避免在循环中修改原序列、避免字符串 += 拼接、警惕 $O(N^2)$ 的隐藏操作。

当你能够熟练地在 for 循环、推导式和 itertools 之间做出最合适的选择时,你的 Python 代码将真正展现出简洁、高效与强大的“Pythonic”魅力。

如果你对特定场景下的循环优化(如多线程/异步循环 async for),或 itertools 中更复杂的组合算法有疑问,欢迎随时深入探讨!