循环语句:for循环、while循环
一句话概述
循环语句让计算机自动重复执行代码,处理批量数据、遍历集合、累积计算等重复性任务。Python 提供两种主要循环结构:for 循环擅长遍历序列(列表、元组、字符串等)中的每一个元素,while 循环则根据条件是否成立来决定是否继续循环。在 AI 开发中,循环用于遍历训练数据、迭代优化模型参数、批量处理数据集等核心场景。
💡 核心要点:① for 循环逐个取出可迭代对象中的元素,处理完自动结束 ② range() 是 for 循环的最佳搭档,快速生成整数序列 ③ while 循环在条件为 True 时持续执行,适合不知道循环次数的场景 ④ 循环是 AI 训练中遍历数据集(epoch/batch)的核心机制
教学与演示
一、for 循环:遍历序列的利器
是什么:for 循环按顺序从可迭代对象(列表、元组、字符串、range 等)中逐个取出元素,赋值给循环变量,然后执行循环体内的代码。当所有元素被遍历完时,循环自动结束。
大白话 就像点名——老师拿着花名册,从第一个同学开始依次点名,点到谁谁站起来回答。for 循环就是程序的花名册,逐个处理列表中的每一项。
为什么:程序经常需要批量处理数据——给全班同学算平均分、给所有图片打水印、给每行文本做分词。如果手动对每个元素写重复代码,既低效又容易出错。for 循环让你写一次逻辑,自动应用到所有元素上。
怎么做:
import numpy as np
# 模拟 AI 训练中的批处理:遍历一个小型数据集
# 生成 5 个模拟数据样本的特征向量(每个样本 3 个特征)
np.random.seed(42) # 固定随机种子,让结果可复现(AI实验的常见做法)
samples = np.random.randint(0, 100, size=(5, 3)) # 生成 5x3 的矩阵
print("📊 数据集(5个样本,每个3个特征):")
print(samples)
print()
# for 循环遍历每一行(每个样本)
print("--- 逐个处理每个样本 ---")
for i, sample in enumerate(samples): # enumerate 同时给出索引和值
# sample 是当前样本的特征向量,是一个包含3个元素的 numpy 数组
total = np.sum(sample) # 计算该样本所有特征值之和(模拟特征聚合)
mean_val = np.mean(sample) # 计算该样本的均值
print(f"样本 {i+1}:{sample} → 总和={total}, 均值={mean_val:.1f}")什么用:在 AI 模型训练中,每个 epoch 都要遍历整个训练集的所有样本——for batch in dataloader: 是 PyTorch 中最常见的循环模式。在图像处理中,for 循环遍历文件夹中的所有图片进行批量预处理(如调整大小、归一化)。在 NLP 中,for 循环遍历文档中的每个句子进行分词处理。
二、range():for 循环的最佳搭档
是什么:range(start, stop, step) 返回一个不可变的整数序列对象,通常与 for 循环配合使用来控制循环次数。它按需生成数字,不占用大量内存——即使 range(1000000) 也不会创建 100 万个整数的列表。
大白话 range 就像体育课上的报数——「从 1 开始,报到 10 结束」。你不需要提前写好 1 到 10 的数字卡片,range 在需要的时候才告诉你下一个数字是什么。
为什么:很多循环不需要遍历已有数据,只需要「重复 N 次」。比如训练 100 个 epoch、生成 50 个随机验证样本、模拟 1000 次蒙特卡洛实验。range() 提供了最简洁的方式来控制固定次数的循环。
怎么做:
import numpy as np
# range(stop):从 0 开始到 stop-1,步长为 1
print("range(5):从0到4")
for i in range(5): # 等价于 [0, 1, 2, 3, 4]
print(f" 第 {i+1} 次迭代", end="")
print("\n")
# range(start, stop):从 start 开始到 stop-1
print("range(2, 7):从2到6")
for i in range(2, 7):
print(f" 值 = {i}", end="")
print("\n")
# range(start, stop, step):指定步长
print("range(0, 10, 2):0到9,每隔2个(偶数)")
for i in range(0, 10, 2):
print(f" 值 = {i}", end="")
print("\n")
# 在实际AI场景中:模拟训练过程的 epoch 迭代
print("--- 模拟AI训练过程 ---")
epochs = 5 # 训练总轮数
for epoch in range(1, epochs + 1): # 从第1轮开始
# 模拟每轮训练的损失值(Loss)逐渐下降
loss = np.exp(-epoch * 0.5) + np.random.uniform(0, 0.1) # 指数衰减 + 随机噪声
print(f"Epoch {epoch}/{epochs} → Loss: {loss:.4f}")
print("训练完成!")什么用:在 AI 中,for epoch in range(num_epochs) 控制模型训练的轮数。在模型评估中,使用 for seed in range(10) 进行多次随机种子实验取平均结果。在超参数搜索(Grid Search)中,range() 遍历学习率、batch size 等超参数的所有候选值。在数据增强中,range(augment_times) 控制每张图片生成多少个增强版本。
三、while 循环:条件驱动的循环
是什么:while 循环在每次迭代前检查条件表达式——如果为 True 则执行循环体,然后再次检查条件;如果为 False 则退出循环。循环次数不确定,完全取决于条件何时变为 False。
大白话 while 就像「吃到饱为止」——你不知道自己会吃几碗,但只要还没饱(条件为 True)就继续吃。for 是「把这盘吃完」,while 是「吃到饱为止」。
为什么:很多场景下,循环次数无法提前确定——游戏要持续运行直到玩家退出、AI 模型要训练到损失不再下降为止、网络请求要重试直到成功。while 循环就是为这种「循环到条件满足」的场景而生。
怎么做:
import numpy as np
# 模拟 AI 模型训练中的 Early Stopping(早停法)机制
# 模型持续训练,直到连续 3 次验证损失不再下降就停止
print("--- 模拟 Early Stopping 训练 ---")
patience = 3 # 容忍次数:连续3次不改善就停止
no_improve_count = 0 # 计数器:记录连续不改善的次数
best_loss = float('inf') # 最佳损失值,初始化为无穷大
epoch = 0 # 当前轮次
while no_improve_count < patience: # 只要连续不改善次数 < 容忍次数,就继续训练
epoch += 1
# 模拟验证损失:整体趋势下降,但有随机波动
current_loss = 0.5 * np.exp(-epoch * 0.3) + np.random.uniform(0, 0.15)
current_loss = round(current_loss, 4)
if current_loss < best_loss: # 损失改善了!
best_loss = current_loss # 更新最佳损失
no_improve_count = 0 # 重置不改善计数器
status = "✅ 改善"
else: # 损失没有改善
no_improve_count += 1 # 不改善计数 +1
status = f"❌ 未改善 ({no_improve_count}/{patience})"
print(f"Epoch {epoch} | Loss: {current_loss:.4f} | Best: {best_loss:.4f} | {status}")
print(f"\n🛑 训练停止!共训练 {epoch} 轮,最佳 Loss: {best_loss:.4f}")什么用:Early Stopping(早停法)是 AI 训练中最经典的 while 循环应用——「当验证损失不再下降时停止训练」,防止过拟合。在强化学习中,while 循环驱动智能体与环境持续交互直到 episode 结束。在数据爬取中,while 循环实现分页抓取直到没有更多数据。在超参数优化(如贝叶斯优化)中,while 循环持续搜索直到达到指定的试验次数或效果不再提升。
四、循环中的 else 子句
是什么:Python 的 for 和 while 循环都支持一个独特的 else 子句——当循环正常结束(没有被 break 打断)时,执行 else 块中的代码。如果循环被 break 提前终止,else 块不会执行。
大白话 就像快递员送包裹——如果所有包裹都成功送达(正常结束),就发「今日配送完毕」通知(else);如果中途遇到问题提前收工(break),就不发这个通知。
为什么:else 子句常用于「搜索未找到」的场景——遍历列表找某个元素,如果找遍了都没找到(正常结束),就在 else 中给出「未找到」的处理。这比设置一个 found 标志变量更加优雅。
怎么做:
import numpy as np
# 模拟在 AI 模型评估指标中查找是否存在过拟合风险
# 生成一组验证损失值
np.random.seed(123)
val_losses = np.random.uniform(0.2, 0.8, size=8) # 8个epoch的验证损失
val_losses = np.round(val_losses, 4)
print("📈 验证损失序列:", val_losses)
# for-else:查找连续上升的迹象(过拟合信号)
# 过拟合的典型信号:验证损失连续上升
print("\n--- 检测过拟合风险 ---")
rise_count = 0
for i in range(1, len(val_losses)): # 从第2个元素开始比较
if val_losses[i] > val_losses[i-1]: # 当前损失 > 上一个,说明在上升
rise_count += 1
print(f" Epoch {i+1}: {val_losses[i]:.4f} > {val_losses[i-1]:.4f} ↑上升 ({rise_count}次)")
if rise_count >= 3: # 连续上升3次,认为过拟合风险较高
print(f"\n⚠️ 连续 {rise_count} 次上升,存在过拟合风险!")
break # 提前终止检查,else 块不会执行
else: # for 正常结束(没有 break)时执行
print(f"\n✅ 未检测到连续3次上升,过拟合风险较低")
# while-else:查找质数(另一种经典用法)
print("\n--- while-else 示例:查找质数 ---")
n = 29 # 要检查的数字
divisor = 2 # 从2开始除
while divisor * divisor <= n: # 只需检查到 sqrt(n)
if n % divisor == 0: # 找到了因数,说明不是质数
print(f"{n} 能被 {divisor} 整除,不是质数")
break # 提前退出
divisor += 1 # 尝试下一个除数
else: # while 正常结束,说明没找到任何因数
print(f"{n} 是质数 ✅")什么用:在 AI 异常检测中,遍历传感器数据序列查找异常点——如果正常遍历完未发现异常(else),则标记数据为正常批次。在模型搜索中,遍历候选模型架构列表寻找满足性能要求的模型——如果全部检查完仍未找到(else),则扩大搜索范围。在数据验证中,检查数据集所有样本是否通过质量检查——全部通过才执行后续训练流程。
概念关系图谱
| 概念 | 核心含义 | 与AI的关系 | 关联概念 |
|---|---|---|---|
| for 循环 | 遍历可迭代对象中的每个元素 | 遍历 DataLoader 中的 batch 数据 | 可迭代对象、in |
| while 循环 | 条件为 True 时持续执行 | Early Stopping、强化学习环境交互 | 条件表达式、break |
| range() | 惰性生成整数序列 | 控制 epoch 轮数、超参数遍历 | for 循环 |
| enumerate() | 同时获取索引和值 | 记录训练步数、batch 编号 | for 循环 |
| 可迭代对象 | 能被 for 遍历的对象 | Dataset、DataLoader 的设计基础 | list、tuple、str |
| 循环 else | 循环正常结束(非break)时执行 | 搜索未找到时的兜底处理 | for、while、break |
| epoch | 训练中完整遍历一次数据集 | 深度学习训练的核心概念 | for、range |
| 迭代 | 重复执行代码块的过程 | 梯度下降的多次参数更新 | 循环 |
重点答疑
Q1: for 循环和 while 循环什么时候用哪个?
简单原则:知道循环次数用 for,不知道用 while。具体来说:遍历列表、字典、文件行等已知集合时用 for;等待某个条件成立(如用户输入正确密码、模型损失降到阈值以下)时用 while。如果硬要用 for 写 while 的场景,就得写 while True: if 条件: break——这其实等于把 while 又请回来了。
Q2: 循环中的 break、continue 和 else 会互相影响吗?
是的。break 会跳出循环,导致 else 块不执行。continue 只是跳过本轮剩余代码进入下一轮,不会影响 else 的执行——只要循环最终是自然结束的(不是被 break 打断的),else 就会执行。可以把 else 理解为「循环没有被 break 打断时的庆祝动作」。
Q3: range(1000000) 会占用很多内存吗?
不会。Python 3 中的 range() 返回的是一个「惰性」序列对象,它只在需要时才计算下一个数字,内存占用极小(无论 range 多大,都只存储 start、stop、step 三个值)。这和 Python 2 中的 range()(返回一个完整的列表)完全不同。不过需要注意的是,如果写成 list(range(1000000)),那确实会创建一个包含 100 万个元素的列表并占用大量内存。
章节单词汇总
| 英文 | 音标 | 术语/释义 |
|---|---|---|
| iteration | /ˌɪtəˈreɪʃən/ | 迭代;循环中的每一次重复执行 |
| loop | /luːp/ | 循环;重复执行代码块的结构 |
| traverse | /trəˈvɜːrs/ | 遍历;逐个访问集合中的每个元素 |
| enumerate | /ɪˈnjuːməreɪt/ | 枚举;同时获取索引和值的遍历方式 |
| iterate | /ˈɪtəreɪt/ | 迭代(动词);在循环中重复处理 |
| range | /reɪndʒ/ | 范围;生成整数序列的内置函数 |
| infinite | /ˈɪnfɪnət/ | 无限的;while True 会形成无限循环 |
| sequence | /ˈsiːkwəns/ | 序列;有顺序的元素集合 |
面试练习
Q1 [单选] for i in range(5): print(i) 输出几个数字?
- A. 6
- B. 5
- C. 4
- D. 0
解答:range(5) 生成 0、1、2、3、4,共 5 个数字。注意 range(n) 从 0 开始,到 n-1 结束,不包含 n。
Q2 [单选] 以下代码的输出是?for i in range(1, 10, 3): print(i, end=" ")
- A.
1 4 7 10 - B.
1 4 7 - C.
1 3 6 9 - D.
1 2 3 4 5 6 7 8 9
解答:range(1, 10, 3)从 1 开始,步长 3,到 10 之前结束:1、4、7。10 超出了 stop 值(不包含),所以输出1 4 7。
Q3 [多选] 关于 Python for 循环,以下哪些说法是正确的?
- A. for 循环可以遍历字符串中的每个字符
- B. for 循环可以遍历列表中的每个元素
- C. for 循环只能遍历数字序列
- D. for 循环配合 range() 可以控制循环次数
解答:C 错误——for 循环可以遍历任何可迭代对象(字符串、列表、元组、字典、集合、文件对象等)。字符串本质上是字符序列,所以 A 正确。
Q4 [单选] 以下哪个 while 循环不会变成死循环?
- A.
while True: print("A") - B.
while 1: print("B") - C.
x = 5; while x > 0: x -= 1 - D.
while "hello": print("D")
解答:C 中 x 初始为 5,每次循环减 1,当 x=0 时条件 x > 0 为 False,循环退出。A 和 B 的条件永远为 True(1 在 Python 中是 truthy),D 的 "hello" 是非空字符串,也是 truthy,都会死循环。
Q5 [单选] for item in [10, 20, 30]: print(item) 中,循环执行几次?
- A. 3次
- B. 2次
- C. 30次
- D. 1次
解答:列表有 3 个元素(10、20、30),for 循环遍历三次,每次 item 依次为 10、20、30。
Q6 [多选] 以下哪些场景适合使用 while 循环而非 for 循环?
- A. 用户登录系统,输入错误密码可以重试直到正确
- B. AI 模型训练直到验证损失不再下降
- C. 遍历一个已知大小的列表求和
- D. 游戏主循环,持续运行直到玩家退出
解答:A(不知道用户会输错几次)、B(不知道需要多少 epoch)、D(不知道游戏会持续多久)都适合 while。C 适合 for——列表大小已知,直接遍历即可。
Q7 [单选] 以下代码输出什么?for i in range(3): print(i) if i == 1: break else: print("完成")
- A.
0 1 2 完成 - B.
0 1 - C.
0 1 - D.
0 1 2
解答:range(3)遍历 i=0、1、2。i=0 时打印 0;i=1 时打印 1 并执行 break 跳出循环。因为循环被 break 打断(非正常结束),所以 else 块不执行。最终输出0 1。注意:实际代码中 else 需要正确的缩进对齐。
Q8 [单选] while True: 循环要如何安全退出?
- A. 等待程序崩溃
- B. 在循环体内使用
break语句配合条件判断 - C. 按 Ctrl+C 是唯一的方法
- D. 循环会自动在 1000 次后退出
解答:while True是故意写的死循环,必须靠循环体内的break配合条件判断来退出。比如while True: 处理数据; if 数据为空: break。Ctrl+C 也能终止但那是外部强制中断,不是程序的正常退出机制。
Q9 [多选] 关于 enumerate() 函数,以下哪些是正确的?
- A.
enumerate(["a", "b"])产生 (0, "a")、(1, "b") - B. 可以通过
start参数指定起始索引 - C. enumerate() 只能用于列表
- D. 常用于需要同时知道元素位置和值的场景
解答:A 正确,默认从 0 开始。B 正确,enumerate(lst, start=1) 从 1 开始。C 错误,enumerate 可用于任何可迭代对象。D 正确,在跟踪训练步数时非常实用。
Q10 [单选] 循环的 else 子句在什么情况下不会执行?
- A. 循环正常完成所有迭代
- B. 循环被 break 提前终止
- C. 循环体中没有 break 语句
- D. 循环体中有 continue 语句
解答:else 不会执行的唯一情况是循环被 break 提前终止。如果循环自然结束(遍历完所有元素,或 while 条件变为 False),else 都会执行。continue 只跳过当前迭代的剩余部分,不影响 else。