第2章 2.4 for 循环与 range()

上一章我们学会了 while 循环,发现它就像天气预报的「明天继续下雨吗?」一样,符合条件就反复执行。但你有没有觉得 while 循环有点「盲」?它只知道问「对不对」,却不知道具体数到哪儿了。比如你想让程序「把 1 到 100 的数字一个个打印出来」,用 while 得手动设个计数器,还得记得每次 +1,一不小心就搞出个死循环。

这一章我们学个更聪明的办法——for 循环配合 range(),就像有人帮你数数,你只管说「从几数到几」就行。


🎯 开场 3 分钟:为什么要学这个?

想象这样一个场景:月底了,你对着 Excel 表格发呆,老板说「把这 500 行数据里每个用户的消费额都加 10%」。你一个个手改?改到明年吧。

或者你想做个「点名器」,随机挑一个同学回答问题,结果 while 循环要么停不下来,要么根本没按预期次数执行。

学完这章你能:

  • 用 for + range() 精确控制循环次数,不再靠「感觉差不多」
  • 遍历列表、字符串等可迭代对象,批量处理数据
  • 用 enumerate 同时拿到索引和值,用 zip 合并两个列表一起遍历

🧱 基础 25 分钟:核心概念

什么是 range()?—— 帮你数数的工具

range() 就像一个报数机器人,你告诉它从几开始、到哪结束,它就帮你生成一串数字。

# range(5) 生成 0 到 4
for i in range(5):
print(i)

输出:

0
1
2
3
4

这太有用了!想象你在排队,range(5) 就是「第 0 个人、第 1 个人……第 4 个人」,不用自己数,它帮你搞定。

range() 的三种打开方式

方式 1:range(stop) —— 从 0 数到 stop-1

for i in range(10):
print(f"第 {i} 次执行")

方式 2:range(start, stop) —— 从 start 数到 stop-1

for i in range(5, 10):
print(i)  # 输出 5, 6, 7, 8, 9

就像「从第 5 节课上到第 9 节课」,包含开头不包含结尾。

方式 3:range(start, stop, step) —— 还能跳着数

for i in range(0, 10, 2):
print(i)  # 输出 0, 2, 4, 6, 8

step=2 就是「每数一个跳过一个」,相当于「偶数序列」。

配图1 - 配图1

什么是 for 循环?—— 流水线上的取货员

如果说 while 是「站门口问对不对」,for 循环就是「沿着流水线一个个取东西」。

水果列表 = ["苹果", "香蕉", "橘子"]

for 水果 in 水果列表:
print(f"正在处理: {水果}")

输出:

正在处理: 苹果
正在处理: 香蕉
正在处理: 橘子

注意!这里的 水果 是变量名,你可以随便起,但一般起有意义的名字。看到这里你可能在想:水果列表里的东西是一个个取出来的,那 range() 生成的数字怎么用 for 取出来?

for i in range(5):
print(f"计数器 i = {i}")

输出:

计数器 i = 0
计数器 i = 1
计数器 i = 2
计数器 i = 3
计数器 i = 4

range(5) 相当于一个「隐藏的列表」[0, 1, 2, 3, 4],for 循环帮你一个个取出来。

enumerate:同时拿到索引和值

有时候你不仅想知道「当前是什么」,还想知道「这是第几个」。比如打印「第 1 名、苹果」「第 2 名、香蕉」:

水果列表 = ["苹果", "香蕉", "橘子"]

for 索引, 水果 in enumerate(水果列表):
print(f"第 {索引 + 1} 个水果是: {水果}")

输出:

第 1 个水果是: 苹果
第 2 个水果是: 香蕉
第 3 个水果是: 橘子

enumerate 会把列表变成「(索引, 值)`元组」的序列,for 循环帮你解开。注意索引从 0 开始,所以要 +1 才符合人类习惯。

配图2 - 配图2

zip:两个列表同步遍历

如果你有两个列表,想「第一个列表的第 0 个配对第二个列表的第 0 个」:

names = ["小明", "小红", "小李"]
scores = [85, 92, 78]

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

输出:

小明的分数是 85
小红的分数是 92
小李的分数是 78

zip 就像「拉链」一样,把两个列表「咬合」在一起。短的那个列表决定了遍历次数——如果列表长度不一样,多余的部分会被忽略。


🔥 实战 35 分钟:3 个递进的小项目

项目 1(5 分钟):自动打印课程表

场景:你是班长,要打印这周前 3 天的课程安排。

# 课程表
课程列表 = ["周一:Python 基础", "周二:数据结构", "周三:算法", 
        "周四:数据库", "周五:网络编程"]

# 用 range 只打印前 3 天
print("=== 本周前 3 天课程 ===")
for i in range(3):
print(课程列表[i])

输出:

=== 本周前 3 天课程 ===
周一:Python 基础
周二:数据结构
周三:算法

解释:range(3) 生成 [0, 1, 2],刚好对应前 3 个课程。这比 while 循环简单多了,不用手动维护计数器。


项目 2(15 分钟):批量计算员工年终奖

场景:公司要根据绩效计算年终奖,绩效 A 发 20% 年终奖,B 发 15%,C 发 10%。从 CSV 读取数据。

# 模拟 CSV 数据(实际可以用 open().readlines() 读真实文件)
员工数据 = [
{"姓名": "张三", "月薪": 8000, "绩效": "A"},
{"姓名": "李四", "月薪": 6000, "绩效": "B"},
{"姓名": "王五", "月薪": 7000, "绩效": "A"},
{"姓名": "赵六", "月薪": 5000, "绩效": "C"},
]

# 绩效奖金比例
奖金比例 = {"A": 0.20, "B": 0.15, "C": 0.10}

print("=== 年终奖明细 ===")
总奖金支出 = 0

for 员工 in 员工数据:
姓名 = 员工["姓名"]
月薪 = 员工["月薪"]
绩效 = 员工["绩效"]

# 年终奖 = 月薪 × 12 × 奖金比例
比例 = 奖金比例[绩效]
年终奖 = 月薪 * 12 * 比例
总奖金支出 += 年终奖

print(f"{姓名}:月薪 {月薪}元,绩效{绩效},年终奖 {年终奖:.2f}元")

print(f"\n公司年度奖金总支出:{总奖金支出:.2f}元")

输出:

=== 年终奖明细 ===
张三:月薪 8000元,绩效A,年终奖 19200.00元
李四:月薪 6000元,绩效B,年终奖 10800.00元
王五:月薪 7000元,绩效A,年终奖 16800.00元
赵六:月薪 5000元,绩效C,年终奖 6000.00元

公司年度奖金总支出:52800.00元

解释:for 循环遍历每个员工的字典,用格式化字符串 f"{年终奖:.2f}" 保留两位小数,% 操作符是老式写法。


项目 3(15 分钟):待办事项管理小工具

场景:做一个命令行待办清单,可以添加任务、查看任务、标记完成。

待办列表 = []

def 显示菜单():
print("\n=== 待办清单 ===")
print("1. 添加任务")
print("2. 查看任务")
print("3. 标记完成")
print("4. 退出")

def 添加任务():
任务 = input("请输入新任务:")
待办列表.append({"任务": 任务, "完成": False})
print(f"已添加:{任务}")

def 查看任务():
if not 待办列表:
    print("列表是空的!")
    return
print("\n当前待办:")
for 索引, 条目 in enumerate(待办列表):
    状态 = "✓" if 条目["完成"] else "○"
    print(f"{索引 + 1}. [{状态}] {条目['任务']}")

def 标记完成():
查看任务()
if not 待办列表:
    return
序号 = int(input("请输入要完成的任务序号:"))
if 1 <= 序号 <= len(待办列表):
    待办列表[序号 - 1]["完成"] = True
    print(f"已完成:{待办列表[序号 - 1]['任务']}")
else:
    print("序号无效!")

# 主循环
while True:
显示菜单()
选择 = input("请选择 (1-4):")

if 选择 == "1":
    添加任务()
elif 选择 == "2":
    查看任务()
elif 选择 == "3":
    标记完成()
elif 选择 == "4":
    print("再见!")
    break
else:
    print("无效选择,请重试!")

运行效果示例:

=== 待办清单 ===
1. 添加任务
2. 查看任务
3. 标记完成
4. 退出
请选择 (1-4):1
请输入新任务:写周报
已添加:写周报

请选择 (1-4):2

当前待办:
1. [○] 写周报

请选择 (1-4):3
1. [○] 写周报
请输入要完成的任务序号:1
已完成:写周报

解释:这个项目组合了 for 循环遍历 + enumerate 获取索引 + while 循环做主菜单。实际开发中可以用文件读写把数据持久化。


💪 进阶 20 分钟:常见坑 + 性能小贴士

坑 1:range() 容易写错边界

# ❌ 错误:想打印 1-10,写成了 range(10)
for i in range(10):
print(i)  # 只会打印 0-9
# ✅ 正确:明确要 1-10,用 range(1, 11)
for i in range(1, 11):
print(i)  # 打印 1-10

记住:range() 是「包头不包尾」,就像「第 1 节课到第 3 节课」指的是 1、2、3,不是 1、2、3、4。

坑 2:修改列表时别用 for 遍历

# ❌ 错误:边遍历边删除可能导致漏删
数字列表 = [1, 2, 3, 4, 5]
for 数字 in 数字列表:
if 数字 < 3:
    数字列表.remove(数字)  # 可能漏掉某些元素
# ✅ 正确:用切片创建副本,或者用列表推导式
数字列表 = [1, 2, 3, 4, 5]
数字列表 = [x for x in 数字列表 if x >= 3]  # 列表推导式
print(数字列表)  # [3, 4, 5]

坑 3:zip() 当列表长度不同时会丢数据

# ❌ 容易忽略:长度不同的列表 zip 会截断
a = [1, 2, 3, 4]
b = ["甲", "乙"]
for 数字, 字母 in zip(a, b):
print(f"{数字}-{字母}")
# 只输出:1-甲, 2-乙,4 被丢弃了
# ✅ 正确:先检查长度,或用 itertools.zip_longest
from itertools import zip_longest

a = [1, 2, 3, 4]
b = ["甲", "乙"]
for 数字, 字母 in zip_longest(a, b, fillvalue="?"):
print(f"{数字}-{字母}")
# 输出:1-甲, 2-乙, 3-?, 4-?

坑 4:enumerate 的索引别当列表下标直接用

# ❌ 错误:以为索引 1 对应第二个元素,但原始列表可能不一致
原始列表 = ["a", "b", "c"]
删除后的列表 = []

for 索引, 值 in enumerate(原始列表):
删除后的列表.append(值)
if 值 == "b":
    del 原始列表[索引]  # 索引已经不对了!

坑 5:for 循环后用 else(Python 特有语法)

# Python 的 for...else:循环正常结束才执行 else
for i in range(3):
print(i)
else:
print("循环正常结束")
# 输出:0, 1, 2, 循环正常结束
# 如果被 break 打断,else 不会执行
for i in range(3):
if i == 1:
    break
print(i)
else:
print("循环正常结束")
# 只输出 0,不输出 else

性能小贴士:列表推导式比 for + append 更快

# ❌ 慢:循环追加
结果 = []
for i in range(1000):
结果.append(i * 2)
# ✅ 快:列表推导式(内部用 C 语言实现)
结果 = [i * 2 for i in range(1000)]

调试技巧:用 enumerate 打印关键变量

# 不知道循环哪里出问题了?把索引和值都打印出来
数据 = ["苹果", "香蕉", "橘子"]

for 索引, 值 in enumerate(数据):
print(f"DEBUG: 索引={索引}, 值={值}, 列表长度={len(数据)}")
if 索引 > 10:  # 防止无限循环
    print("疑似死循环,强制退出")
    break

✏️ 练习题

练习 1(2 分钟):数到哪了?

  • 输入:无
  • 预期输出:打印 5 到 15 的所有整数
  • 提示:range() 的第二个参数是结束值,要+1
for i in range(5, 16):
print(i)

练习 2(2 分钟):偶数之和

  • 输入:无
  • 预期输出:计算 1-10 所有偶数的和,输出 偶数之和为: 30
  • 提示:用 range(2, 11, 2) 生成偶数
total = 0
for i in range(2, 11, 2):
total += i
print(f"偶数之和为: {total}")

练习 3(3 分钟):成绩统计

  • 输入:见代码
  • 预期输出:打印不及格(<60)的人数
  • 提示:for 循环遍历列表,加个 if 判断
分数列表 = [55, 78, 92, 45, 88, 60, 73]
不及格人数 = 0

for 分数 in 分数列表:
if 分数 < 60:
    不及格人数 += 1

print(f"不及格人数:{不及格人数}")

练习 4(3 分钟):enumerate 实战

  • 输入:列表 ["猫", "狗", "鸟"]
  • 预期输出:1. 猫 2. 狗 3. 鸟(每个一行)
  • 提示:索引要 +1 才符合从 1 开始的人类习惯
动物列表 = ["猫", "狗", "鸟"]
for 索引, 动物 in enumerate(动物列表, 1):
print(f"{索引}. {动物}")

练习 5(5 分钟):找错题

  • 输入:以下代码
  • 预期输出:分析为什么只打印到 0-8 而不是 0-9
  • 提示:range() 的包头不包尾特性
# 这段代码期望打印 0-9,但实际只打印到 8
# 请找出原因并修复

for i in range(0, 10, 1):  # 实际上 range(10) 就够了
print(i)

作业:做一个「个人消费记账本」

需求描述
做一个命令行记账工具,记录你的日常消费,月底统计各类支出占比。

功能点
1. 添加消费记录(日期、分类、金额、备注)
2. 查看所有记录(带序号)
3. 按分类统计总支出
4. 显示支出最高的 Top 3 分类

加分项
1. 数据保存到文件(下次启动还能用)
2. 支持删除某条记录

验收标准
- 能跑起来
- 输入数据后能正确统计
- 代码有中文注释

提交方式:评论区贴代码或 GitHub 链接


📚 总结 + 资源

这章学了啥
- range() 帮你生成数字序列,是 for 循环的黄金搭档
- for 变量 in 可迭代对象 是 Python 最常用的循环形式
- enumerate 拿索引、zip 合并遍历,让数据处理更灵活

延伸学习
- 官方文档:内置类型 - 序列类型
- 书籍:《Python编程:从入门到实践》第 9 章
- 视频:B 站小甲鱼《零基础学 Python》第 35-40 集

互动钩子
你在工作/学习中有没有遇到「要重复处理一堆数据」的场景?是用什么方法解决的?评论区聊聊,老粉优先回复!


下一章我们要综合运用 for 循环和 while 循环,做两个经典实战项目:猜数字游戏九九乘法表,看看两种循环怎么配合使用才能威力最大化。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。