11. 使用 while 循环重复执行操作
程序经常需要多次重复执行某些操作。你已经在第 8 章看到如何使用 if 语句做出决策,但如果你需要反复执行某个动作,直到满足某个条件为止,该怎么办?这就是循环(loop)发挥作用的地方。
Python 提供两种主要的循环:while 循环和 for 循环。在本章中,我们将重点介绍 while 循环,它会在条件保持为真时重复执行一段代码。更适合用来遍历序列的 for 循环,将在第 12 章介绍。
理解 while 循环是编写程序的基础,这类程序可以反复处理数据、验证用户输入、实现游戏循环,以及处理许多其他真实世界的编程场景。
11.1) while 循环的结构
while 循环(while loop) 会在指定条件求值为 True 时反复执行一段代码块。一旦条件变为 False,循环就会停止,程序继续执行循环之后的代码。
基本 while 循环语法
while 循环的结构如下所示:
while condition:
# 要重复的代码块
# 只要 condition 为 True,这段代码就会运行condition 可以是任何会求值为布尔值的表达式(或者可以解释为真值或假值,正如我们在第 7 章学习的那样)。while 语句下方缩进的代码块称为循环体(loop body),只要条件保持为 True,它就会反复执行。
我们来看一个简单示例:
# 从 1 数到 5
count = 1
while count <= 5:
print(f"Count is: {count}")
count = count + 1 # 递增 count
print("Loop finished!")Output:
Count is: 1
Count is: 2
Count is: 3
Count is: 4
Count is: 5
Loop finished!一步一步来看它是如何工作的:
- 我们把
count初始化为1 - 检查条件
count <= 5。由于1 <= 5为True,循环体执行 - 在循环内部,我们打印当前的 count,然后将其加 1
- 循环体完成后,Python 返回到
while语句并再次检查条件 - 这个过程会重复,直到
count变为6,此时6 <= 5为False,循环终止 - 程序继续执行循环之后的代码
关键点是:条件会在每次迭代(iteration)(循环体的每次重复)之前进行检查。如果条件一开始就是 False,循环体根本不会执行:
count = 10
while count <= 5:
print("This will never print")
print("Loop skipped entirely")Output:
Loop skipped entirely由于 10 <= 5 从一开始就是 False,循环体从不运行。
修改循环变量的重要性
为了让 while 循环最终停止,循环内部必须有某些操作把条件从 True 变为 False。这通常意味着要修改条件中使用的变量。如果你忘记这样做,就会创建一个无限循环(infinite loop)(我们将在下一节详细讨论)。
下面这个示例演示了为什么更新循环变量很重要:
# 计算从 1 到 10 的数字之和
total = 0
number = 1
while number <= 10:
total = total + number # 将当前数字加到 total
number = number + 1 # 移动到下一个数字
print(f"The sum of numbers from 1 to 10 is: {total}")Output:
The sum of numbers from 1 to 10 is: 55在这个示例中,我们在遍历数字的同时累积求和。total 和 number 都会在每次迭代中变化,但正是 number 的修改确保了当 number 变为 11 时循环最终会终止。
带用户输入的 while 循环
while 循环一个很实用的用途是:持续处理用户输入,直到满足特定条件为止。我们来创建一个简单的猜数字游戏:
# 简单的猜数字游戏
secret_number = 7
guess = 0
while guess != secret_number:
guess = int(input("Guess the number (1-10): "))
if guess < secret_number:
print("Too low! Try again.")
elif guess > secret_number:
print("Too high! Try again.")
else:
print("Correct! You guessed it!")这个循环会持续要求用户猜测,直到用户输入正确的数字为止。每次迭代都会处理一次猜测并给出反馈。当 guess 等于 secret_number 时,循环自然终止,使条件 guess != secret_number 变为 False。
带多个条件的 while 循环
你可以使用布尔运算符(and、or、not)来创建更复杂的循环条件,正如我们在第 9 章学到的那样:
# 处理输入,直到用户输入 "quit" 或者达到 5 次尝试
attempts = 0
user_input = ""
while user_input != "quit" and attempts < 5:
user_input = input("Enter a command (or 'quit' to exit): ")
attempts += 1
if user_input == "quit":
print("Goodbye!")
else:
print(f"You entered: {user_input}")
print(f"Attempts remaining: {5 - attempts}")
if attempts >= 5 and user_input != "quit":
print("Maximum attempts reached.")只要两个条件都为真:用户还没有输入 "quit" 并且尝试次数没有超过 5 次,这个循环就会继续。当任一条件变为假时,循环终止。
可视化 while 循环的执行过程
下面的流程图展示了 while 循环是如何执行的:
循环会创建一个周期:检查条件、若条件为真则执行循环体、更新变量,然后再次检查条件。这个周期会持续,直到条件变为假。
11.2) 无限循环以及如何避免它们
无限循环(infinite loop) 是指永远不会终止的循环,因为它的条件永远不会变为 False。无限循环是初学者在使用 while 循环时最常见的错误之一,它们会导致程序无限期卡住。
什么会导致无限循环?
无限循环最常见的原因是:忘记修改会影响循环条件的变量。我们来看一个有问题的示例:
# 警告:无限循环——仅用于演示
# 问题:count 从未被修改
count = 1
while count <= 5:
print(f"Count is: {count}")
# Missing: count += 1如果你运行这段代码,它会永远打印 "Count is: 1",因为 count 始终保持为 1,而 1 <= 5 永远为 True。条件从不改变。
如何识别这是一个无限循环: 查看循环条件(count <= 5),然后检查循环体内部是否有任何内容修改了 count。如果没有,并且条件一开始为 True,那你就有一个无限循环。
下面是修正后的版本:
# 正确版本:进行适当的递增
count = 1
while count <= 5:
print(f"Count is: {count}")
count += 1 # 这确保循环最终会终止Output:
Count is: 1
Count is: 2
Count is: 3
Count is: 4
Count is: 5使用安全上限调试无限循环
在开发包含循环的代码时,在测试期间添加一个安全上限有助于防止意外的无限循环:
# 开发阶段的安全上限
count = 1
iterations = 0
max_iterations = 100 # 安全上限
while count <= 5 and iterations < max_iterations:
print(f"Count is: {count}")
count += 1
iterations += 1
if iterations >= max_iterations:
print("WARNING: Maximum iterations reached. Check for infinite loop.")这种模式增加了一个计数器来跟踪循环执行了多少次。如果它达到安全上限,就说明你的循环逻辑可能出了问题。一旦你确信循环能正确工作,就可以移除这个安全上限。
带用户输入的无限循环
另一个与无限循环相关的常见场景是用户输入校验:
# 警告:可能出现无限循环——仅用于演示
# 问题:如果用户从不输入有效内容,循环就永远不会结束
age = -1
while age < 0:
age = int(input("Enter your age: "))
# 如果用户输入负数,循环会继续如果用户最终输入一个非负数,这个循环就能正确工作;但如果用户一直输入负值,它就会成为无限循环。虽然对某些程序来说这可能是可以接受的(你希望一直询问直到收到有效输入),但理解这一点很重要:循环能否终止完全取决于用户行为。
一个更健壮的方法可能包含退出方式:
# 更好的方式:提供退出选项
age = -1
while age < 0:
user_input = input("Enter your age (or 'quit' to exit): ")
if user_input.lower() == 'quit':
print("Exiting program.")
age = 0 # 设置为有效值以退出循环
else:
age = int(user_input)
if age < 0:
print("Age must be non-negative. Please try again.")
if age > 0:
print(f"Your age is: {age}")由逻辑错误导致的无限循环
有时无限循环是由于更新变量时出现了逻辑错误:
# 警告:无限循环——仅用于演示
# 问题:count 被递减而不是递增
count = 1
while count <= 5:
print(f"Count is: {count}")
count -= 1 # 错误:这会让 count 变小,而不是变大这会创建一个无限循环,因为 count 从 1 开始变为 0、-1、-2 等。由于负数总是小于或等于 5,条件 count <= 5 将永远保持为 True。
修正后的版本:
count = 1
while count <= 5:
print(f"Count is: {count}")
count += 1 # 正确:递增以最终超过 5调试无限循环
如果你在运行 Python 脚本时不小心创建了无限循环,你可以在终端中按 Ctrl+C(或 Mac 上的 Cmd+C)停止它。这会向 Python 发送键盘中断信号,从而停止程序。
如何避免无限循环:
- 始终确保循环条件有可能变为 False:检查条件里的变量是否在循环内部被修改
- 使用正确的比较运算符:确保
<=、<、!=等符合你的意图 - 先用小数值测试:在运行可能执行很多次的循环之前,先用较小的上限测试
- 添加调试打印语句:临时打印循环变量,看看它如何变化:
- 在开发期间使用安全上限:如前所示,在测试时添加最大迭代次数计数器
11.3) 在 while 循环中使用 break 和 continue
Python 提供了两个特殊语句,让你对循环执行有更强的控制能力:break 和 continue。这些语句允许你根据执行过程中出现的条件来改变循环的正常流程。
11.3.1) break 语句
break 语句会立即终止循环,不管循环条件是什么。当 Python 遇到 break 时,它会完全退出循环,并继续执行循环之后的代码。
下面是一个简单示例:
# 当达到某个特定值时退出循环
count = 1
while count <= 10:
if count == 5:
print("Reached 5, stopping loop")
break
print(f"Count: {count}")
count += 1
print("Loop exited")Output:
Count: 1
Count: 2
Count: 3
Count: 4
Reached 5, stopping loop
Loop exited注意:当 count 等于 5 时,break 语句执行,循环立即终止。即使条件 count <= 10 仍然会是 True,循环也永远不会到达 count = 6。
break 如何改变循环流程
理解 break 如何改变正常的循环执行至关重要。下面的流程图展示了其中的差异:
关键点:break 提供了一条从循环中立即退出的路径,绕过循环体剩余代码以及条件检查。
break 的实际用途:输入校验
break 最常见的用途之一是在收到有效输入时退出循环:
# 持续询问,直到收到有效输入为止
while True:
age_input = input("Enter your age (must be positive): ")
# 尝试转换为整数
try:
age = int(age_input)
# 检查是否有效
if age > 0:
print(f"Thank you! Your age is {age}")
break # 在输入有效时退出循环
else:
print("Age must be positive. Please try again.")
except ValueError:
print("That's not a valid number. Please try again.")
print("Input validation complete")这种模式使用 while True: 创建一个有意的无限循环,然后在收到有效输入时用 break 退出。它比试图管理一个复杂的循环条件更简洁。(注意:这里我们使用了 try 和 except,我们会在第 VII 部分详细学习。现在只需要理解:它用于捕获将输入转换为整数时发生的错误。)
带多个条件的 break
你可以将 break 与复杂条件结合使用,在满足多个条件之一时退出循环:
# 在用户输入中搜索某个特定项
search_term = "python"
attempts = 0
max_attempts = 5
while attempts < max_attempts:
user_input = input("Enter a word (or 'quit' to exit): ").lower()
attempts += 1
if user_input == 'quit':
print("User requested exit")
break
if user_input == search_term:
print(f"Found '{search_term}'!")
break
print(f"'{user_input}' is not '{search_term}'. Try again.")
print(f"Attempts remaining: {max_attempts - attempts}")
if attempts >= max_attempts:
print("Maximum attempts reached")这个循环可以通过三种方式退出:
- 用户输入 "quit"(第一个
break) - 用户输入搜索词(第二个
break) - 达到最大尝试次数(循环条件变为
False)
11.3.2) continue 语句
continue 语句会跳过当前这次迭代的剩余部分,并回到循环条件检查处。不同于会完全退出循环的 break,continue 只是跳到下一次迭代。
下面是一个基础示例:
# 只打印 1 到 10 的奇数
count = 0
while count < 10:
count += 1
if count % 2 == 0: # 如果是偶数
continue # 跳过剩余部分,进入下一次迭代
print(f"Odd number: {count}")Output:
Odd number: 1
Odd number: 3
Odd number: 5
Odd number: 7
Odd number: 9它的工作方式:
count在每次迭代开始时递增- 如果
count是偶数(count % 2 == 0),就会执行continue continue会跳过print语句,并跳回while条件- 如果
count是奇数,continue不执行,于是print语句运行
continue 如何改变循环流程
下面的流程图展示了 continue 如何影响循环执行:
break 与 continue 的关键区别:
- break:完全退出循环,跳到循环之后的代码
- continue:跳过当前迭代的剩余代码,跳回条件检查
continue 的重要放置位置说明
注意,count += 1 位于 continue 之前。如果我们把它放在后面,偶数会触发 continue 跳过递增,从而导致无限循环:
# 警告:无限循环——仅用于演示
# 问题:对于偶数,continue 会跳过递增
count = 0
while count < 10:
if count % 2 == 0:
continue # 跳过下面所有内容,包括 count += 1
count += 1 # 错误:对偶数来说,这行永远不会运行
print(f"Odd number: {count}")当 count 为 0(偶数)时,continue 执行,跳过 count += 1。循环再次检查 0 < 10,然后无限重复。
规则:始终确保会影响条件的循环变量在任何可能跳过循环体其余部分的 continue 语句之前被更新。
continue 的实际用途:过滤数据
当你在处理数据且希望跳过某些项时,continue 非常有用:
# 只处理有效的分数
score_count = 0
total_score = 0
attempts = 0
while attempts < 5:
score_input = input(f"Enter score {attempts + 1} (or 'skip' to skip): ")
attempts += 1
if score_input.lower() == 'skip':
print("Skipping this score")
continue # 跳到下一次迭代
try:
score = int(score_input)
if score < 0 or score > 100:
print("Score must be between 0 and 100. Skipping.")
continue # 跳过无效分数
# 有效分数——处理它
total_score += score
score_count += 1
print(f"Score recorded: {score}")
except ValueError:
print("Invalid input. Skipping.")
continue
if score_count > 0:
average = total_score / score_count
print(f"\nAverage of {score_count} valid scores: {average:.1f}")
else:
print("\nNo valid scores entered")这个示例演示了 continue 的多种用法:
- 当用户输入 "skip" 时跳过
- 当分数超出有效范围时跳过
- 当输入不是有效数字时跳过
每个 continue 都会阻止该分数被加入总分,但循环仍会继续到下一次尝试。
结合使用 break 和 continue
你可以在同一个循环中同时使用 break 和 continue 来实现更复杂的控制:
# 处理数字,直到总和超过 100,跳过负数
total = 0
count = 0
while True:
number_input = input("Enter a number (or 'done' to finish): ")
if number_input.lower() == 'done':
print("User finished entering numbers")
break # 退出循环
try:
number = int(number_input)
if number < 0:
print("Negative numbers not allowed. Skipping.")
continue # 跳到下一次迭代
total += number
count += 1
print(f"Added {number}. Current total: {total}")
if total > 100:
print("Total exceeded 100. Stopping.")
break # 达到上限时退出循环
except ValueError:
print("Invalid input. Skipping.")
continue
print(f"\nFinal total: {total} (from {count} numbers)")这个循环演示了:
- 用户输入 "done" 时用
break退出 - 用
continue跳过负数 - 用
continue跳过无效输入 - 当总和超过 100 时用
break退出
11.4) 在 while 循环中使用 else
Python 有一个许多其他编程语言都没有的独特特性:你可以给 while 循环附加一个 else 子句。这个 else 代码块只会在循环正常完成时执行(也就是循环条件变为 False,且没有遇到 break 语句)。
while 搭配 else 的基本语法
语法如下所示:
while condition:
# 循环体
else:
# 仅当循环正常完成时才执行
# (没有被 break 中断)我们来看一个简单示例:
# 使用 else 子句从 1 数到 5
count = 1
while count <= 5:
print(f"Count: {count}")
count += 1
else:
print("Loop completed normally")
print("Program continues")Output:
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5
Loop completed normally
Program continueselse 代码块会执行,因为循环一直运行到条件(count <= 5)变为 False。循环在没有被中断的情况下“正常”完成。
else 何时不会执行:break 语句
else 子句的关键行为是:如果循环是被 break 语句终止的,它就不会执行:
# 使用 break 搜索一个数字
count = 1
target = 3
while count <= 5:
print(f"Checking: {count}")
if count == target:
print(f"Found {target}!")
break
count += 1
else:
print("Target not found in range")
print("Search complete")Output:
Checking: 1
Checking: 2
Checking: 3
Found 3!
Search complete注意:else 代码块("Target not found in range")并没有执行,因为循环被 break 终止了。这是关键区别:else 只在循环正常退出(条件变为 False)时运行,而不是通过 break 退出时运行。
现在我们来看看当目标没有找到时会发生什么:
# 搜索一个不存在的数字
count = 1
target = 7 # 不在 1-5 范围内
while count <= 5:
print(f"Checking: {count}")
if count == target:
print(f"Found {target}!")
break
count += 1
else:
print("Target not found in range")
print("Search complete")Output:
Checking: 1
Checking: 2
Checking: 3
Checking: 4
Checking: 5
Target not found in range
Search complete这一次,循环在没有找到目标的情况下完成了所有迭代,因此条件 count <= 5 最终变为 False,else 代码块执行。
else 如何配合循环完成执行
下面的流程图展示了带 else 子句时的执行路径:
只有当循环条件自然变为 False 时,才会到达 else 代码块。如果遇到 break,流程会直接跳过 else 代码块,进入循环之后的代码。
实际用途:搜索操作
else 子句在搜索操作中特别有用,你通常希望知道某个东西是否被找到:
# 在多次尝试中查找有效密码
valid_password = "python123"
max_attempts = 3
attempts = 0
while attempts < max_attempts:
password = input(f"Enter password (attempt {attempts + 1}/{max_attempts}): ")
attempts += 1
if password == valid_password:
print("Access granted!")
break
else:
print("Access denied. Maximum attempts exceeded.")
print("Account locked.")如果用户输入了正确密码,break 执行并跳过 else 代码块。如果所有尝试都用完仍未成功,循环正常完成,else 代码块执行,表示失败。
else 与 continue
continue 语句不会阻止 else 代码块执行。只有 break 才会阻止:
# continue 不会影响 else 的执行
count = 0
while count < 5:
count += 1
if count == 3:
print(f"Skipping {count}")
continue # 跳到下一次迭代
print(f"Processing {count}")
else:
print("Loop completed normally (continue doesn't prevent this)")Output:
Processing 1
Processing 2
Skipping 3
Processing 4
Processing 5
Loop completed normally (continue doesn't prevent this)else 代码块会执行,因为循环正常完成了。continue 只影响单次迭代,不影响整体循环是否完成。
对比:有 break 与没有 break 时的 else
我们来做一个并排对比:
# 示例 1:寻找第一个能被 7 整除的数(带 break)
print("Finding first number divisible by 7:")
number = 1
while number <= 20:
if number % 7 == 0:
print(f"Found: {number}")
break
number += 1
else:
print("No number divisible by 7 found in range")
print()
# 示例 2:检查所有数字(没有 break)
print("Checking all numbers for divisibility by 7:")
number = 1
while number <= 20:
if number % 7 == 0:
print(f"Found: {number}")
number += 1
else:
print("Finished checking all numbers")Output:
Finding first number divisible by 7:
Found: 7
Checking all numbers for divisibility by 7:
Found: 7
Found: 14
Finished checking all numbers在第一个示例中,break 在找到第一个匹配项后停止循环,因此 else 不执行。在第二个示例中,循环检查所有数字并正常完成,因此 else 执行。
何时在 while 循环中使用 else
else 子句最有用的场景是:
- 搜索操作:你想知道是否找到了某个东西
- 限制尝试次数的校验:你需要处理所有尝试都耗尽的情况
- 带提前退出的处理流程:你希望对“全部完成”与“提前停止”采取不同的行为
然而,对于来自其他语言的程序员(那些语言没有这个特性)来说,循环里的 else 可能会令人困惑。有时用一个标志变量会更清晰:
# 使用 else 子句
attempts = 0
while attempts < 3:
password = input("Enter password: ")
attempts += 1
if password == "secret":
print("Access granted")
break
else:
print("Access denied")
# 使用标志变量的等价写法(有时更清晰)
attempts = 0
access_granted = False
while attempts < 3:
password = input("Enter password: ")
attempts += 1
if password == "secret":
print("Access granted")
access_granted = True
break
if not access_granted:
print("Access denied")两种方式都可行。请选择在你的具体场景中能让代码更清晰的写法。
理解 while 循环搭配 else 的用法,会为你提供另一种编写清晰、表达力强的代码的工具,尤其是在搜索与校验场景中,你需要区分“找到”与“未找到”或“成功”与“尝试全部用尽后的失败”。
在本章中,我们深入探索了 while 循环,学习了如何:
- 构造基于条件重复执行代码的循环
- 通过正确更新循环变量来避免无限循环
- 在需要时使用
break提前退出循环 - 根据条件使用
continue跳过某些迭代 - 使用
else处理循环在没有break的情况下正常完成的情况
这些工具让你能够强力控制程序中的重复执行。在下一章中,我们将学习 for 循环,它提供了一种更便捷的方式来遍历字符串、列表(list)和范围等序列。