第2章 2.2 循环:for/while/for...in
上一章我们学会了用
if做判断,用三元运算符做二选一。但你有没有遇到过这种情况:老师让你把 1 到 100 的偶数打印出来,你第一反应是写 50 个print()?或者要从 1000 条数据里找出符合条件的条目,总不能写 1000 个if吧?这一章我们要解决的问题就是:如何让计算机自动做重复的事。
你有没有发现,生活中大部分事情都是重复的?每天早上起床、洗漱、吃早饭、出门——这就是一个循环。程序也是一样,大部分时间我们都在让计算机重复执行某些操作。循环就是编程里的「复制粘贴」按钮,一旦写好,计算机就会帮你执行 N 遍。
学完这一章,你能:
- 用 for 遍历任何数据列表
- 用 while 处理不知道要循环多少次的情况
- 用 break 和 continue 控制循环的走向
- 写出一个能实际使用的小工具(比如处理 Excel 数据、筛选文件)
🧱 基础 25 分钟:核心概念
什么是循环?为什么要用循环?
类比\n\n
\n\n
\n\n:想象你在一个自助餐厅,菜单上有 10 道菜。你不会一道道跟服务员说「我要第 1 道」「我要第 2 道」——你只需要说「这 10 道,我每道都要尝一口」。循环就是这个意思:告诉计算机「这一堆东西,我每一个都要处理」。
痛点:没有循环时,处理 100 条数据要写 100 行代码;有循环后,3 行搞定。
for 循环:已知次数的循环
最常用的循环方式。当你知道要循环多少次,或者有一个明确的列表要遍历时,用 for。
# 打印 1 到 5
for i in range(1, 6):
print(f"第 {i} 次打印")
第 1 次打印
第 2 次打印
第 3 次打印
第 4 次打印
第 5 次打印
解释:range(1, 6) 生成 1, 2, 3, 4, 5 这 5 个数字,for i in 就是「把每个数字代进 i 里跑一遍」。
注意!
range(1, 6)实际上是 1 到 5,不包含 6。这是因为 Python 的惯例是「左闭右开」——左边包含,右边不包含。记不住?那就记住:range 几个数,就写比最后一个大 1 的数。
遍历列表:for 的真正威力
for 最强大的地方是可以遍历任何「一堆东西」——列表、字符串、字典都行。
fruits = ["苹果", "香蕉", "橙子", "葡萄"]
for fruit in fruits:
print(f"我喜欢吃 {fruit}")
我喜欢吃 苹果
我喜欢吃 香蕉
我喜欢吃 橙子
我喜欢吃 葡萄
解释:fruit 这个变量名是你自己起的,每一轮循环,它会自动变成列表里的下一个元素。
while 循环:不知道要跑多少次
有时候你不知道要循环多少次,只知道「满足条件就一直跑」。比如:掷骰子,直到掷到 6 为止。
import random
result = 0
count = 0
while result != 6:
result = random.randint(1, 6)
count += 1
print(f"第 {count} 次,掷到 {result}")
print(f"终于掷到 6 了!总共掷了 {count} 次")
第 1 次,掷到 3
第 2 次,掷到 5
第 3 次,掷到 2
第 4 次,掷到 6
终于掷到 6 了!总共掷了 4 次
解释:while result != 6 的意思是「只要 result 不等于 6,就一直循环」。每次循环都会重新掷骰子,直到命中 6。
坑来了! while 循环一定要有结束条件,否则会变成「死循环」,程序卡死在那不动。如果你不确定会不会死循环,先想清楚「什么情况下会退出」。
for...in:Python 特有的遍历方式
Python 没有 for...of(那是 JavaScript 的语法),但 Python 的 for...in 更直观:
# 遍历字符串(每个字符)
message = "Hello"
for char in message:
print(char)
H
e
l
l
o
# 遍历字典(默认只遍历 key)
person = {"name": "小明", "age": 18, "city": "北京"}
for key in person:
print(f"{key} = {person[key]}")
name = 小明
age = 18
city = 北京
# 遍历字典的值
for value in person.values():
print(value)
小明
18
北京
# 同时遍历 key 和 value
for key, value in person.items():
print(f"{key}: {value}")
name: 小明
age: 18
city: 北京
break 和 continue:控制循环的走向
有时候你不需要跑完所有循环,想提前退出——用 break。有时候你不想处理某个特定情况,跳过它——用 continue。
# 找出第一个能被 3 整除的数,找到就退出
numbers = [1, 5, 7, 9, 11, 15, 21]
for num in numbers:
if num % 3 == 0:
print(f"找到了!第一个能被 3 整除的数是 {num}")
break
print(f"{num} 不能被 3 整除,继续找...")
1 不能被 3 整除,继续找...
5 不能被 3 整除,继续找...
7 不能被 3 整除,继续找...
找到了!第一个能被 3 整除的数是 9
# 跳过所有奇数,只处理偶数
for i in range(1, 11):
if i % 2 == 1: # 奇数
continue # 跳过这次循环,直接进入下一次
print(f"{i} 是偶数")
2 是偶数
4 是偶数
6 是偶数
8 是偶数
10 是偶数
区别:break 是「彻底不玩了,退出整个循环」;continue 是「这次不算,重新来过」。
🔥 实战 35 分钟:3 个递进的小项目
项目 1:成绩统计器(5 分钟)
场景:老师有一份学生成绩单,想快速知道有多少人及格、平均分是多少。
# 学生成绩列表
scores = [85, 92, 78, 60, 45, 88, 73, 95, 67, 54]
# 统计及格人数
pass_count = 0
total_score = 0
for score in scores:
if score >= 60:
pass_count += 1
total_score += score
print(f"总人数: {len(scores)}")
print(f"及格人数: {pass_count}")
print(f"不及格人数: {len(scores) - pass_count}")
print(f"平均分: {total_score / len(scores):.2f}")
输出:
总人数: 10
及格人数: 7
不及格人数: 3
平均分: 73.70
一句话解释:for score in scores 遍历每个成绩,累加的同时统计及格人数。
项目 2:CSV 文件数据清洗(15 分钟)
场景:你从网上下载了一份 CSV 格式的销售数据,里面有一些无效数据(价格为 0 或负数、日期格式不对),需要清洗后再使用。
假设当前目录下有一个
sales.csv文件,内容如下:
日期,商品,销量,单价
2024-01-01,iPhone,50,6999
2024-01-02,MacBook,20,12999
2024-01-03,AirPods,100,-1
2024-01-04,iPad,35,2999
2024-01-05,Apple Watch,0,2999
2024-01-06,AirPods,80,1999
import csv
from datetime import datetime
def清洗数据():
valid_records = [] # 存放清洗后的有效数据
error_records = [] # 存放出错的数据
# 读取 CSV 文件
with open('sales.csv', 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
try:
# 验证日期格式
date = datetime.strptime(row['日期'], '%Y-%m-%d')
# 验证销量(必须是正整数)
sales = int(row['销量'])
if sales <= 0:
raise ValueError("销量必须大于 0")
# 验证单价(必须是正数)
price = float(row['单价'])
if price <= 0:
raise ValueError("单价必须大于 0")
# 所有验证通过,添加到有效列表
valid_records.append({
'日期': date.strftime('%Y-%m-%d'),
'商品': row['商品'],
'销量': sales,
'单价': price,
'销售额': sales * price
})
except Exception as e:
error_records.append({
'原始数据': row,
'错误原因': str(e)
})
# 输出结果
print(f"=== 数据清洗报告 ===")
print(f"有效数据: {len(valid_records)} 条")
print(f"无效数据: {len(error_records)} 条\n")
print("--- 无效数据详情 ---")
for err in error_records:
print(f"数据: {err['原始数据']}")
print(f"原因: {err['错误原因']}\n")
print("--- 有效数据统计 ---")
total_revenue = sum(r['销售额'] for r in valid_records)
print(f"总收入: ¥{total_revenue:,.2f}")
# 按商品汇总
product_summary = {}
for record in valid_records:
product = record['商品']
if product not in product_summary:
product_summary[product] = 0
product_summary[product] += record['销售额']
print("\n--- 商品销售额排行 ---")
for product, revenue in sorted(product_summary.items(), key=lambda x: x[1], reverse=True):
print(f"{product}: ¥{revenue:,.2f}")
return valid_records, error_records
# 运行清洗程序
if __name__ == '__main__':
清洗数据()
输出:
=== 数据清洗报告 ===
有效数据: 5 条
无效数据: 2 条
--- 无效数据详情 ---
数据: {'日期': '2024-01-03', '商品': 'AirPods', '销量': '100', '单价': '-1'}
原因: 单价必须大于 0
数据: {'日期': '2024-01-05', '商品': 'Apple Watch', '销量': '0', '单价': '2999'}
原因: 销量必须大于 0
--- 有效数据统计 ---
总收入: ¥1,399,870.00
--- 商品销售额排行 ---
MacBook: ¥259,980.00
iPhone: ¥349,950.00
AirPods: ¥159,920.00
iPad: ¥104,965.00
一句话解释:csv.DictReader 帮我们逐行读取数据,用 for 遍历每一行并验证,不符合条件就跳过或记录错误。
项目 3:待办事项管理小工具(15 分钟)
场景:做一个命令行待办清单,可以添加任务、查看任务、完成任务、删除任务。数据保存在本地文件里。
import json
import os
from datetime import datetime
TODO_FILE = 'todos.json'
def 加载任务():
"""从文件加载待办事项,如果文件不存在就返回空列表"""
if os.path.exists(TODO_FILE):
with open(TODO_FILE, 'r', encoding='utf-8') as f:
return json.load(f)
return []
def 保存任务(todos):
"""保存待办事项到文件"""
with open(TODO_FILE, 'w', encoding='utf-8') as f:
json.dump(todos, f, ensure_ascii=False, indent=2)
def 显示任务(todos):
"""显示所有任务"""
if not todos:
print("📝 暂无待办事项,添加一个吧!")
return
print(f"\n📋 共 {len(todos)} 个待办事项:")
print("-" * 50)
for index, todo in enumerate(todos, 1):
status = "✅" if todo['done'] else "⬜"
priority_tag = {
'high': '🔴',
'medium': '🟡',
'low': '🟢'
}.get(todo.get('priority', 'medium'), '🟡')
print(f"{index}. {status} {priority_tag} {todo['title']}")
if todo.get('created'):
print(f" 创建于: {todo['created']}")
print("-" * 50)
def 添加任务(todos, title, priority='medium'):
"""添加新任务"""
todo = {
'title': title,
'done': False,
'priority': priority,
'created': datetime.now().strftime('%Y-%m-%d %H:%M')
}
todos.append(todo)
print(f"✅ 已添加: {title}")
def 完成任务(todos, index):
"""标记任务为完成"""
if 1 <= index <= len(todos):
todos[index - 1]['done'] = True
print(f"✅ 已完成: {todos[index - 1]['title']}")
else:
print("❌ 无效的任务编号")
def 删除任务(todos, index):
"""删除任务"""
if 1 <= index <= len(todos):
removed = todos.pop(index - 1)
print(f"🗑️ 已删除: {removed['title']}")
else:
print("❌ 无效的任务编号")
def 主菜单():
"""显示主菜单"""
print("\n" + "=" * 50)
print("📌 待办事项管理器")
print("=" * 50)
print("1. 查看所有任务")
print("2. 添加新任务")
print("3. 完成任务")
print("4. 删除任务")
print("5. 退出")
print("=" * 50)
def main():
todos = 加载任务()
print("👋 欢迎使用待办事项管理器!")
while True:
主菜单()
choice = input("请选择操作 (1-5): ").strip()
if choice == '1':
显示任务(todos)
保存任务(todos)
elif choice == '2':
title = input("请输入任务内容: ").strip()
if title:
print("优先级: 1-高 2-中(默认) 3-低")
p_choice = input("请选择优先级: ").strip()
priority_map = {'1': 'high', '3': 'low'}
priority = priority_map.get(p_choice, 'medium')
添加任务(todos, title, priority)
保存任务(todos)
else:
print("❌ 任务内容不能为空")
elif choice == '3':
显示任务(todos)
if todos:
index = input("请输入要完成的任务编号: ").strip()
if index.isdigit():
完成任务(todos, int(index))
保存任务(todos)
elif choice == '4':
显示任务(todos)
if todos:
index = input("请输入要删除的任务编号: ").strip()
if index.isdigit():
删除任务(todos, int(index))
保存任务(todos)
elif choice == '5':
保存任务(todos)
print("👋 再见!数据已保存。")
break
else:
print("❌ 无效选择,请输入 1-5")
if __name__ == '__main__':
main()
预期输出(交互示例):
👋 欢迎使用待办事项管理器!
==================================================
📌 待办事项管理器
==================================================
1. 查看所有任务
2. 添加新任务
3. 完成任务
4. 删除任务
5. 退出
==================================================
请选择操作 (1-5): 2
请输入任务内容: 完成 Python 循环章节的学习
优先级: 1-高 2-中(默认) 3-低
请选择优先级: 1
✅ 已添加: 完成 Python 循环章节的学习
==================================================
📌 待办事项管理器
==================================================
1. 查看所有任务
2. 添加新任务
3. 完成任务
4. 删除任务
5. 退出
==================================================
请选择操作 (1-5): 1
📋 共 1 个待办事项:
--------------------------------------------------
1. ⬜ 🔴 完成 Python 循环章节的学习
建于: 2024-01-15 14:30
--------------------------------------------------
一句话解释:用 while True 保持程序运行,用 for 遍历显示任务列表,所有数据存在 JSON 文件里。
💪 进阶 20 分钟:常见坑 + 性能小贴士
坑 1:修改列表时不要用 for 遍历
# ❌ 错误示例:一边遍历一边删除,可能漏掉元素
nums = [1, 2, 3, 4, 5, 6]
for n in nums:
if n % 2 == 0:
nums.remove(n)
print(nums) # [1, 3, 5],漏掉了 4
# ✅ 正确做法:遍历副本
nums = [1, 2, 3, 4, 5, 6]
for n in nums[:]: # nums[:] 是副本,不影响原列表
if n % 2 == 0:
nums.remove(n)
print(nums) # [1, 3, 5],正确删除所有偶数
坑 2:while 循环一定要有退出条件
# ❌ 死循环示例
# n = 1
# while n > 0:
# print(n)
# n += 1 # 永远不会小于等于 0,会无限打印
# ✅ 正确做法:确保有退出条件
n = 1
while n <= 10:
print(n)
n += 1
坑 3:range 容易搞错边界
# ❌ 常见错误:以为 range(10) 会包含 10
for i in range(10):
print(i) # 只打印 0-9,不包含 10
# ✅ 正确做法:想包含 10 就用 range(11)
for i in range(11):
print(i) # 打印 0-10
坑 4:字符串拼接用 + 很低效
# ❌ 低效做法:每次循环都创建新字符串
result = ""
for i in range(1000):
result += str(i)
# ✅ 高效做法:用列表 + join
parts = []
for i in range(1000):
parts.append(str(i))
result = "".join(parts)
坑 5:忘记缩进
# ❌ 错误示例:缩进不一致或缺少缩进
# for i in range(5):
# print(i) # 缺少缩进,会报错
# ✅ 正确做法:确保循环体有 4 个空格缩进
for i in range(5):
print(i) # 正确缩进
性能小贴士:能用列表推导式就别用 for 循环
列表推导式是 Python 的特色,写法简洁而且速度更快:
# 普通 for 循环(3 行)
squares = []
for i in range(10):
squares.append(i ** 2)
# 列表推导式(1 行,效果一样)
squares = [i ** 2 for i in range(10)]
调试技巧:用 enumerate 同时拿到索引和值
fruits = ["苹果", "香蕉", "橙子"]
# ❌ 笨办法:手动维护索引
for i in range(len(fruits)):
print(f"{i}: {fruits[i]}")
# ✅ 优雅办法:用 enumerate
for i, fruit in enumerate(fruits, start=1): # start=1 让索引从 1 开始
print(f"{i}: {fruit}")
✏️ 练习题
练习 1(2 分钟):改数字,找规律
- 输入:把项目 1 的及格线从 60 改成 90
- 预期输出:新的及格人数和不及格人数
- 提示:只改一个数字就行
练习 2(3 分钟):加个判断
- 输入:在练习 1 基础上,统计 90 分以上的「优秀生」人数
- 预期输出:额外打印「优秀生人数: X」
- 提示:用一个新的
if score >= 90判断
练习 3(10 分钟):处理新数据
- 输入:有一份新的成绩单
[88, 45, 92, 33, 76, 61, 55],找出所有不及格的分数 - 预期输出:
[45, 33, 55]或类似的列表 - 提示:用
for遍历 +if判断,把不及格的加入新列表
练习 4(15 分钟):串起来
- 输入:把练习 3 的逻辑封装成一个函数
找出不及格(成绩列表),并调用它 - 预期输出:调用函数返回正确结果
- 提示:函数定义用
def 找出不及格(scores):,最后return结果
练习 5(5 分钟):看图找 bug
- 输入:下面这段代码的运行结果是什么?
nums = [1, 2, 3, 4, 5]
for num in nums:
if num == 3:
break
print(num)
- 预期输出:打印 1 和 2,遇到 3 就 break 退出
- 提示:想想
break的作用是什么
作业:做一个「个人预算记录工具」
需求描述:做一个命令行工具,帮助用户记录每天的消费支出,并能按月统计。
功能点:
1. 添加支出(金额、类别、备注、日期)
2. 查看所有支出记录
3. 按月统计总支出
4. 找出支出最高的类别
加分项:
1. 支持数据导出为 CSV 文件
2. 添加「预算提醒」功能(当月支出超过设定额度时警告)
验收标准:
- 能添加、查看支出记录
- 能按月统计
- 数据保存到本地文件,程序重启后不丢失
- 代码有适当注释
提交方式:评论区贴代码或 GitHub 链接
📚 总结
这一章我们学了 3 个核心点:
1. for 循环:适合遍历已知的数据集(列表、字符串、字典)
2. while 循环:适合不知道要循环多少次、靠条件判断退出的场景
3. 控制流:break 提前退出,continue 跳过当前 iteration
延伸学习资源:
- 官方文档:Python 官方文档 - 循环
- 视频推荐:B 站小甲鱼《零基础入门学习 Python》第 11-12 集
- 书籍:《Python编程:从入门到实践》第 4 章「if 语句和循环」
你在工作中有没有遇到过「这件事明明很简单,却要手动做很多遍」的情况? 比如每周要整理一次数据报表、每月要汇总一次考勤记录。评论区聊聊,下一章我们要学的「函数」就是来解决这个问题的——把重复的代码封装起来,下次一键调用。

评论(0)