第2章 2.3 while 循环与死循环

🎯 为什么要学这个?

你有没有遇到过这种情况——

「我要一直刷到这个视频点赞超过 1000!」

或者

「密码输入错误?没关系,我等下再试,反正无限次机会!」

这些场景,用编程语言来描述,就是「满足某个条件之前,一直重复做某件事」

上一章我们学了 match-case,它让我们能像查字典一样快速匹配不同的模式。但它解决的是「一次性判断」的问题——

如果我想让程序一直干一件事,直到用户喊停呢?

比如:
- 做一个计时器,每秒更新一次,直到用户按 Ctrl+C
- 写一个密码验证器,输入错误就继续问,直到输入对为止
- 做一个行情监控器,每隔 5 秒刷新价格,直到用户退出

这种「满足条件就一直干」的场景,match-case 搞不定,普通的 if 也搞不定。

今天学的 while 循环,就是专门解决这个问题的。

学完本文,你将能独立写出:
- 自动重试的请求工具
- 带倒计时的互动程序
- 简单的游戏主循环


🧱 基础:while 循环是什么?

2.3.1 先看一个生活类比

想象你站在电梯门前,电梯门上写着:

门保持关闭状态,直到有人按上楼按钮

你盯着门,发现:
- 初始状态:门是关着的
- 判断条件:有人按按钮了吗?
- 如果没按:继续等着(循环体)
- 如果按了:门打开(退出循环)

这就是 while 循环的逻辑:

门是关着的
while 没人按按钮:
继续等着
门打开了

2.3.2 Python 语法就这么简单

# 初始化条件
count = 0

# 条件为 True 时,一直执行循环体
while count < 3:
print(f"第 {count + 1} 次:还在循环里...")
count = count + 1  # 别忘了改变条件,否则变成死循环

print("循环结束了!")

运行结果:

第 1 次:还在循环里...
第 2 次:还在循环里...
第 3 次:还在循环里...
循环结束了!

解释:
- count = 0:设置一个计数器,记录循环次数
- while count < 3::如果 count 小于 3,就进入循环
- count = count + 1:每次循环结束,把计数器加 1

注意! 如果你忘记写 count = count + 1,程序会永远停在循环里——这就是传说中的死循环

2.3.3 计数器:循环的「进度条」

计数器就是循环的脚步——告诉程序「你跑了多远了,还剩多少」。

# 倒计时发射火箭
countdown = 5

while countdown > 0:
print(f"🚀 倒计时:{countdown} 秒")
countdown = countdown - 1

print("🔥 发射!")

运行结果:

🚀 倒计时:5 秒
🚀 倒计时:4 秒
🚀 倒计时:3 秒
🚀 倒计时:2 秒
🚀 倒计时:1 秒
🔥 发射!

配图1 - 配图1

2.3.4 break:提前退出循环

有时候我们不想等条件自己变假,而是想主动喊停

# 猜数字游戏
secret_number = 7
guess = 0

while guess != secret_number:
guess = int(input("猜一个 1-10 的数字:"))
if guess == secret_number:
    print("🎉 恭喜猜对了!")
    break  # 猜对了就退出循环
else:
    print(f"不对,继续猜!")

print("游戏结束")

运行示例:

猜一个 1-10 的数字:3
不对,继续猜!
猜一个 1-10 的数字:7
🎉 恭喜猜对了!
游戏结束

解释:
- break:立即跳出循环,不再执行后面的代码
- 相当于循环里的「紧急出口」

2.3.5 continue:跳过本次循环

有时候遇到某种情况,我们想跳过这一次,直接进入下一次

# 打印 1-10,但跳过所有偶数
num = 0

while num < 10:
num = num + 1
if num % 2 == 0:  # 如果是偶数
    continue      # 跳过本次循环,直接下一次
print(num)        # 只打印奇数

运行结果:

1
3
5
7
9

解释:
- continue:跳过循环体剩下的代码,直接进入下一次判断
- num % 2 == 0:判断是否为偶数(% 是取余数的意思)

2.3.6 while-else:循环正常结束才执行的代码

Python 的 while 循环还有个独特语法——else 块。

# 模拟用户登录
attempts = 3
password = "python123"

while attempts > 0:
guess = input("请输入密码:")
if guess == password:
    print("✅ 登录成功!")
    break
attempts = attempts - 1
print(f"❌ 密码错误,还剩 {attempts} 次机会")
else:
# 只有循环正常结束(没有被 break)才会执行这里
print("🚫 次数用完,账户已锁定")

运行示例:

请输入密码:123456
❌ 密码错误,还剩 2 次机会
请输入密码:python123
✅ 登录成功!

或者:

请输入密码:123456
❌ 密码错误,还剩 2 次机会
请输入密码:wrong
❌ 密码错误,还剩 1 次机会
请输入密码:again
🚫 次数用完,账户已锁定

解释:
- 如果循环被 break 退出,else 不执行
- 如果循环是自然条件变假而退出,else 执行

配图2 - 配图2

2.3.7 死循环:最常见的坑

死循环就是条件永远为 True,程序永远不会自己停下来。

# ❌ 错误示例:忘记改变条件
count = 0
while count < 3:
print("我会永远打印下去...")  # count 永远是 0,永远不会 >= 3

正确写法:

# ✅ 正确示例:记得更新计数器
count = 0
while count < 3:
print(f"这是第 {count + 1} 次")
count = count + 1  # 每次循环都让 count 变大

print("循环正常结束")

什么时候可以用死循环?

有些场景确实需要死循环,比如服务器主循环:

# 服务器主循环:一直运行,直到被强制终止
while True:
connection = accept_connection()
handle_request(connection)

说白了while True 就是「不管怎样都继续」,通常配合 break 使用。


🔥 实战:3 个递进项目

项目 1:自动刷新行情的小工具(5 分钟)

场景:你想监控一个价格,价格低于某个值就提醒你,否则每 5 秒刷新一次。

import time

# 模拟行情数据(实际可以用真实 API)
prices = [100, 98, 95, 97, 94, 93, 92, 90]
target_price = 91
index = 0

print(f"📊 监控价格:当前目标 ${target_price}")
print("按 Ctrl+C 可退出监控\n")

while True:
current_price = prices[index % len(prices)]  # 循环使用列表
index = index + 1

print(f"[{index}] 当前价格:${current_price}", end="")

if current_price <= target_price:
    print(" ✅ 价格到了!买入!")
    break
else:
    print(f" ❌ 还没到,继续监控...")

time.sleep(1)  # 暂停 1 秒(实际可能是 5 秒)

预期输出:

📊 监控价格:当前目标 $91
按 Ctrl+C 可退出监控

[1] 当前价格:$100 ❌ 还没到,继续监控...
[2] 当前价格:$98 ❌ 还没到,继续监控...
[3] 当前价格:$95 ❌ 还没到,继续监控...
[4] 当前价格:$97 ❌ 还没到,继续监控...
[5] 当前价格:$94 ❌ 还没到,继续监控...
[6] 当前价格:$93 ❌ 还没到,继续监控...
[7] 当前价格:$92 ❌ 还没到,继续监控...
[8] 当前价格:$90 ✅ 价格到了!买入!

解释:用 while True 做主循环,配合 break 在条件满足时退出,用 time.sleep 控制刷新频率。


项目 2:CSV 数据清洗脚本(15 分钟)

场景:你有一份 CSV 文件,里面有用户的订单数据,但有些行是无效的(比如金额为负数或用户名为空),你需要过滤掉这些无效数据。

假设 orders.csv 内容:

username,amount,status
小明,150,completed
小红,0,pending
张三,200,completed
,100,completed
李四,-50,completed
王五,180,pending
# 读取 CSV 文件并清洗数据
with open('orders.csv', 'r', encoding='utf-8') as f:
lines = f.readlines()

# 跳过表头,从第二行开始处理
valid_count = 0
invalid_count = 0
invalid_reasons = []

index = 0
while index < len(lines):
line = lines[index].strip()

# 跳过空行
if not line:
    index = index + 1
    continue

# 跳过表头
if index == 0:
    index = index + 1
    continue

# 解析数据
parts = line.split(',')
username = parts[0].strip()
amount = parts[1].strip()
status = parts[2].strip()

# 验证数据
is_valid = True
reason = ""

if not username:  # 用户名为空
    is_valid = False
    reason = "用户名为空"

try:
    amount_num = float(amount)
    if amount_num <= 0:  # 金额必须大于 0
        is_valid = False
        reason = f"金额 {amount} 不合理"
except ValueError:
    is_valid = False
    reason = f"金额 {amount} 不是数字"

# 输出结果
if is_valid:
    valid_count = valid_count + 1
    print(f"✅ 有效订单:{username} - ${amount} - {status}")
else:
    invalid_count = invalid_count + 1
    invalid_reasons.append(reason)
    print(f"❌ 无效数据(第 {index + 1} 行):{reason}")

index = index + 1

# 统计报告
print("\n" + "=" * 40)
print(f"📊 数据清洗完成")
print(f"✅ 有效订单:{valid_count} 条")
print(f"❌ 无效数据:{invalid_count} 条")

预期输出:

✅ 有效订单:小明 - $150 - completed
❌ 无效数据(第 2 行):金额 0 不合理
✅ 有效订单:张三 - $200 - completed
❌ 无效数据(第 4 行):用户名为空
❌ 无效数据(第 5 行):金额 -50 不合理
✅ 有效订单:王五 - $180 - pending

========================================
📊 数据清洗完成
✅ 有效订单:3 条
❌ 无效数据:3 条

解释:用 while 循环逐行处理 CSV 文件,配合 if 条件判断数据是否有效,统计有效和无效的数量。


项目 3:待办事项管理器(15 分钟)

场景:做一个命令行的待办事项管理工具,用户可以:
1. 添加待办事项
2. 查看所有待办
3. 标记完成
4. 删除待办
5. 退出程序

# 待办事项管理器
todos = []

print("📝 欢迎使用待办事项管理器")
print("=" * 30)

while True:
print("\n请选择操作:")
print("1. 添加待办")
print("2. 查看待办列表")
print("3. 标记完成")
print("4. 删除待办")
print("5. 退出程序")

choice = input("\n请输入选项(1-5):").strip()

if choice == "1":
    # 添加待办
    task = input("请输入待办事项:").strip()
    if task:
        todos.append({"task": task, "done": False})
        print(f"✅ 已添加:「{task}」")
    else:
        print("❌ 待办事项不能为空")

elif choice == "2":
    # 查看列表
    if not todos:
        print("📭 暂无待办事项")
    else:
        print(f"\n📋 待办列表(共 {len(todos)} 项):")
        index = 1
        while index <= len(todos):
            item = todos[index - 1]
            status = "✅" if item["done"] else "⬜"
            done_mark = " [已完成]" if item["done"] else ""
            print(f"  {index}. {status} {item['task']}{done_mark}")
            index = index + 1

elif choice == "3":
    # 标记完成
    if not todos:
        print("📭 暂无待办事项")
    else:
        try:
            num = int(input("请输入要完成的序号:"))
            if 1 <= num <= len(todos):
                todos[num - 1]["done"] = True
                print(f"✅ 已标记完成:「{todos[num - 1]['task']}」")
            else:
                print("❌ 序号超出范围")
        except ValueError:
            print("❌ 请输入数字")

elif choice == "4":
    # 删除待办
    if not todos:
        print("📭 暂无待办事项")
    else:
        try:
            num = int(input("请输入要删除的序号:"))
            if 1 <= num <= len(todos):
                removed = todos.pop(num - 1)
                print(f"🗑️ 已删除:「{removed['task']}」")
            else:
                print("❌ 序号超出范围")
        except ValueError:
            print("❌ 请输入数字")

elif choice == "5":
    # 退出
    print("\n👋 感谢使用,待办事项管理器已退出!")
    if todos:
        remaining = sum(1 for t in todos if not t["done"])
        print(f"📊 你还有 {remaining} 项待办未完成")
    break

else:
    print("❌ 无效选项,请输入 1-5")

print("\n程序结束")

预期输出(部分演示):

📝 欢迎使用待办事项管理器
===============================

请选择操作:
1. 添加待办
2. 查看待办列表
3. 标记完成
4. 删除待办
5. 退出程序

请输入选项(1-5):1
请输入待办事项:买牛奶
✅ 已添加:「买牛奶」
请输入选项(1-5):1
请输入待办事项:写周报
✅ 已添加:「写周报」
请输入选项(1-5):2

📋 待办列表(共 2 项):
1. ⬜ 买牛奶
2. ⬜ 写周报
请输入选项(1-5):3
请输入要完成的序号:1
✅ 已标记完成:「买牛奶」
请输入选项(1-5):2

📋 待办列表(共 2 项):
1. ✅ 买牛奶 [已完成]
2. ⬜ 写周报
请输入选项(1-5):5

👋 感谢使用,待办事项管理器已退出!
📊 你还有 1 项待办未完成

程序结束

解释:用 while True 做主循环,配合 if-elif-else 处理不同选项,用 break 退出程序。


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

坑 1:忘记更新计数器,变成死循环

# ❌ 错误:count 永远是 0
count = 0
while count < 3:
print("永远打印")
# 程序卡住

# ✅ 正确:每次循环更新计数器
count = 0
while count < 3:
print(f"第 {count + 1} 次")
count = count + 1

坑 2:while 条件写错,导致多执行一次或少执行一次

# ❌ 错误:会打印 0-4,而不是 0-3
count = 0
while count <= 3:
print(count)
count = count + 1
# 输出:0, 1, 2, 3, 4

# ✅ 正确:严格小于才进入循环
count = 0
while count < 3:
print(count)
count = count + 1
# 输出:0, 1, 2

坑 3:break 和 continue 混用时搞不清逻辑

# 打印 1-10,跳过 5,但打印到 6 就停
num = 0
while num < 10:
num = num + 1
if num == 5:
    continue  # 跳过 5,直接下一次
if num == 7:
    break     # 遇到 7 就退出
print(num)
# 输出:1, 2, 3, 4, 6

坑 4:在 while 循环里修改列表时出错

# ❌ 错误:边遍历边删除,可能漏掉元素
fruits = ["苹果", "香蕉", "樱桃", "香蕉", "葡萄"]
i = 0
while i < len(fruits):
if fruits[i] == "香蕉":
    fruits.pop(i)  # 删除后列表变短,但 i 没变
else:
    i = i + 1
# 可能出问题

# ✅ 正确:创建新列表
fruits = ["苹果", "香蕉", "樱桃", "香蕉", "葡萄"]
fruits = [f for f in fruits if f != "香蕉"]
# 或者:
while "香蕉" in fruits:
fruits.remove("香蕉")

坑 5:while-else 的 else 容易被忽略

# 找第一个大于 5 的数,找到就 break
numbers = [1, 3, 4, 2, 6, 8]
index = 0
while index < len(numbers):
if numbers[index] > 5:
    print(f"找到了:{numbers[index]}")
    found = True
    break
index = index + 1
else:
# 如果循环正常结束(没被 break),才会执行这里
found = False
print("没找到大于 5 的数")

print(f"最终结果:found = {found}")

性能小贴士:减少循环内的函数调用

# ❌ 低效:每次循环都调用 len()
items = list(range(100))
i = 0
while i < len(items):  # 每次都要计算 len()
print(items[i])
i = i + 1

# ✅ 高效:先把长度存起来
items = list(range(100))
length = len(items)  # 只计算一次
i = 0
while i < length:
print(items[i])
i = i + 1

调试技巧:用 print 追踪变量值

# 在关键位置打印变量值,帮助理解循环执行过程
count = 0
while count < 3:
print(f"[DEBUG] count = {count}, 条件 = {count < 3}")
count = count + 1
print(f"[DEBUG] 执行后 count = {count}")

print(f"[DEBUG] 循环结束,count = {count}")

输出:

[DEBUG] count = 0, 条件 = True
[DEBUG] 执行后 count = 1
[DEBUG] count = 1, 条件 = True
[DEBUG] 执行后 count = 2
[DEBUG] count = 2, 条件 = True
[DEBUG] 执行后 count = 3
[DEBUG] 循环结束,count = 3

✏️ 练习题

练习 1(1 分钟):抄改计数器

# 把下面的循环改成倒序输出 5 到 1
count = 1
while count <= 5:
print(count)
count = count + 1
  • 输入:直接运行
  • 预期输出5 4 3 2 1(每行一个)
  • 提示:改 while 条件和 count 的更新方式

练习 2(2 分钟):加 if 判断

在练习 1 的基础上,只打印奇数。

  • 输入:直接运行
  • 预期输出5 3 1(每行一个)
  • 提示:用 % 取余判断奇偶,不符合就 continue

练习 3(3 分钟):处理新数据

用项目 2 的方法,创建一个 students.csv,包含 name,score,grade 三列,处理逻辑:只保留 score >= 60 的学生,并在最后统计通过率。

  • 输入:创建 CSV 文件并运行代码
  • 预期输出:显示通过的学生列表和通过率百分比
  • 提示:把项目 2 的 amount 改成 score,判断条件改成 >= 60

练习 4(3 分钟):串接项目 2 和项目 3

把项目 2 的「数据清洗」结果,用项目 3 的「待办列表」方式展示:清洗出哪些有效订单,把它们加入待办清单。

  • 输入:沿用 orders.csv 数据
  • 预期输出:把有效订单作为待办事项显示
  • 提示:把清洗后的 valid_count 条数据转换成待办列表格式

练习 5(3 分钟):分析死循环

下面代码会卡住,说明原因并修复:

num = 10
while num > 0:
print(num)
num = num + 1  # 这里写错了
  • 输入:运行代码
  • 预期输出:程序会一直打印 > 10 的数字,不会停止
  • 提示num + 1 应该是 num - 1

作业:做一个「数字猜谜游戏」

需求描述:
做一个猜数字游戏,程序随机生成 1-100 的整数,玩家有 5 次猜测机会,每次提示「太大了」或「太小了」,猜对或用完次数游戏结束。

功能点:
1. 程序随机生成答案
2. 玩家输入猜测,系统反馈
3. 显示剩余次数
4. 游戏结束时显示结果(猜对/猜错)

加分项:
1. 记录猜测历史(每次猜了什么)
2. 猜错后提示距离答案还差多少

验收标准:
- 能跑起来
- 输入 1-100 的数字有正确反馈
- 5 次用完自动退出
- 代码有注释

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


📚 总结

本文学了 3 个核心点:
1. while 循环的语法:初始化 → 条件判断 → 循环体 → 更新计数器
2. break 提前退出、continue 跳过本次、else 正常结束才执行
3. 死循环的成因:忘记更新计数器条件永远为 True

延伸学习资源:
- 官方文档:while 语句 — 权威语法说明
- 书籍:《Python编程:从入门到实践》第 9 章 — 循环的更多例子
- 视频:B 站「小甲鱼 Python 教程」第 38 讲 — 生动讲解 while 循环


你在什么时候用过 while 循环?

我先说一个我的——以前写爬虫的时候,用 while True 配合 break 做「自动重试」:请求失败就继续请求,直到成功或者重试次数用完。

你在 XX 场景用过这个吗?评论区聊聊,老粉优先回复!

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