跳到主要内容

while 循环

while 循环在条件为真时反复执行一段代码。与 for 适合"遍历已知集合"不同,while 更适合 循环次数未知、依赖条件变化 的场景,比如读取输入、轮询状态、迭代收敛等。

while 基本语法

while 后跟布尔表达式,只要为 True 就反复执行缩进代码块:

count = 0
while count < 5:
print(f"第 {count + 1} 次循环")
count += 1

输出:

第 1 次循环
第 2 次循环
第 3 次循环
第 4 次循环
第 5 次循环

:::warning 不要忘记更新条件变量 while 循环必须确保条件最终会变为 False,否则会无限循环。如果忘记写 count += 1,上面的代码会一直打印"第 1 次循环"。 :::

无限循环

while True: 可以构造无限循环,配合 break 在合适时机跳出:

while True:
user_input = input("请输入命令(输入 quit 退出):")
if user_input == "quit":
print("再见!")
break
print(f"你输入了:{user_input}")

这种写法常用于交互式程序、游戏主循环、服务器请求处理等场景。

:::tip 何时用 while True 当循环条件复杂、需要在循环体中部判断退出时,while True: + break 比把所有条件塞进 while 后面更清晰。例如读取输入直到满足某条件:

while True:
value = int(input("请输入正数:"))
if value > 0:
break
print("输入无效,必须为正数。")

:::

while...else 语句

for 一样,while 也可以带 else 子句,仅在循环 因条件为 False 而正常退出(没有被 break 打断)时执行:

n = 0
while n < 5:
print(n, end=" ")
n += 1
else:
print("\n循环正常结束。")
# 输出:
# 0 1 2 3 4
# 循环正常结束。

一个实际应用是查找元素:

def find_first_even(numbers: list[int]) -> int | None:
"""返回第一个偶数,找不到返回 None。"""
i = 0
while i < len(numbers):
if numbers[i] % 2 == 0:
return numbers[i]
i += 1
else:
print("未找到偶数。")
return None

find_first_even([1, 3, 5, 6]) # 返回 6
find_first_even([1, 3, 5]) # 打印"未找到偶数。"并返回 None

:::note while...else 的争议 while...else 的语义"非 break 退出时执行 else"对初学者不太直观。Python 之禅也强调"可读性很重要"。在大型项目中,更推荐用标志变量或函数提前返回来替代,避免使用 while...else。 :::

避免死循环

死循环是 while 最容易出现的 bug。常见原因:

  1. 忘记更新条件变量:循环体没改变条件中使用的变量。
  2. 更新方向错误:本应递增却写成了递减。
  3. 浮点数比较误差:条件可能永远满足。
# 反例 1:忘记更新 count
# count = 0
# while count < 5:
# print(count) # 无限循环!永远打印 0

# 反例 2:浮点数累加误差
total = 0.0
step = 0.1
target = 1.0
# 这种写法可能因浮点误差永远达不到 target
while total != target:
total += step
if total > 10: # 安全保护
print("异常终止")
break

:::warning 浮点数比较陷阱 不要用 == 比较浮点数。0.1 + 0.1 + 0.1 在 Python 中等于 0.30000000000000004,不严格等于 0.3。应使用容差比较或整数计数:

# 推荐做法:用整数计数
n = 0
while n < 10:
total = n * 0.1
print(total)
n += 1

:::

为防止死循环,可以为循环加上"最大迭代次数"的安全阀:

max_iterations = 1000
iteration = 0
while some_condition() and iteration < max_iterations:
do_something()
iteration += 1

if iteration >= max_iterations:
print("警告:达到最大迭代次数,可能未收敛。")

海象运算符在 while 中的应用

Python 3.8 引入的海象运算符 :=(赋值表达式)能在表达式内部完成赋值,特别适合 while 循环读取输入或处理可迭代值。

传统写法

text = input("输入内容:")
while text != "quit":
print(f"你输入了:{text}")
text = input("输入内容:")

需要写两次 input(),重复且容易出错。

使用海象运算符

while (text := input("输入内容:")) != "quit":
print(f"你输入了:{text}")

一行完成"读取并判断",代码更紧凑。

处理分块数据

海象运算符在分块读取数据时也很有用:

def read_lines(data: list[str], chunk_size: int) -> None:
"""每次读取 chunk_size 行并处理。"""
index = 0
while (chunk := data[index:index + chunk_size]):
print(f"处理块:{chunk}")
index += chunk_size


read_lines(["a", "b", "c", "d", "e"], chunk_size=2)

输出:

处理块:['a', 'b']
处理块:['c', 'd']
处理块:['e']

当切片为空时,chunk 为空列表(falsy),循环自动终止。

:::tip 海象运算符的适用场景 海象运算符特别适合以下场景:

  • while 中读取输入并判断
  • 列表推导式中需复用计算结果
  • 条件表达式中需要保存中间值

但它会让代码更紧凑,也要注意不要为了"炫技"而牺牲可读性。 :::

实战:猜数字游戏

综合运用 while 循环、海象运算符、条件判断,实现一个经典的猜数字游戏:

import random


def guess_number_game() -> None:
"""猜数字游戏:随机生成 1~100 的数字,玩家猜测直到猜中。"""
target = random.randint(1, 100)
attempts = 0
max_attempts = 7

print("欢迎来到猜数字游戏!")
print(f"我已经想好了一个 1~100 之间的数字,你有 {max_attempts} 次机会。")

while attempts < max_attempts:
attempts += 1
remaining = max_attempts - attempts

try:
guess = int(input(f"第 {attempts} 次猜测(剩余 {remaining} 次):"))
except ValueError:
print("请输入有效的整数。")
attempts -= 1 # 不消耗机会
continue

if guess == target:
print(f"恭喜!你用 {attempts} 次猜中了数字 {target}。")
return
elif guess < target:
print(f"太小了!剩余 {remaining} 次。")
else:
print(f"太大了!剩余 {remaining} 次。")

print(f"很遗憾,机会用尽。正确答案是 {target}。")


if __name__ == "__main__":
guess_number_game()

示例运行:

欢迎来到猜数字游戏!
我已经想好了一个 1~100 之间的数字,你有 7 次机会。
第 1 次猜测(剩余 6 次):50
太小了!剩余 6 次。
第 2 次猜测(剩余 5 次):75
太大了!剩余 5 次。
第 3 次猜测(剩余 4 次):63
恭喜!你用 3 次猜中了数字 63。

:::info 二分查找策略 在 1~100 之间,用二分查找(每次猜中间值)最多需要 ⌈log₂100⌉ = 7 次。所以本游戏给 7 次机会,掌握二分策略的玩家总能赢。 :::

:::tip 使用 try-except 处理输入 用户输入是不可控的,应该用 try-except 捕获 ValueError,避免程序因非法输入崩溃。这里用了 continue 跳过本次循环且不消耗机会。 :::

小结

  • while 适合循环次数未知、依赖条件变化的场景。
  • while True: + break 适合在循环体中部判断退出的场景。
  • while...else 在循环正常结束时执行,但语义不直观,慎用。
  • 必须确保循环条件最终为 False,避免死循环;可加最大迭代次数作为安全阀。
  • 浮点数比较要用容差,不要直接用 ==
  • 海象运算符 := 能简化"读取-判断"循环,是 Python 3.8+ 的实用特性。