Python 循环控制与综合应用
困难3循环控制进阶:让程序变得更聪明、更稳健
在编程中,循环帮我们重复做事情,但有时候我们需要在循环中途跳出来、跳过某次循环、或者只在循环正常结束时才执行某段代码。Python 提供了几个好用的工具:break、continue、以及循环的 else 子句。另外,把循环嵌套起来(放在另一个循环里面),再配合这些控制工具,就能解决很多实际生活中遇到的有趣问题。比如:老师点名(循环每一个学生),找零钱组合(循环所有硬币面值),猜数字游戏(循环猜拳直到猜对或次数用尽)。下面我们一步步来学习。
一、循环的 else 子句:只有完整跑完才“吹哨”
1. 它干什么用的?
for 和 while 循环后面都可以跟一个 else 块。这个 else 块只在 循环正常结束(没有因为 break 而提前退出)时才会执行。如果循环被 break 打断了,else 块就不执行。
想象一下:你每天放学后要在操场上跑 5 圈(循环 5 次)。如果中途下雨了,你躲进教室(相当于 break),那么跑圈就没有完成,老师不会给你奖励(else 不执行)。如果你坚持跑完了 5 圈(自然结束),老师就奖励你一颗糖(else 执行)。
2. 生活中的例子:检查全班同学有没有人忘带作业
你负责检查 10 个同学的作业。你从第 1 个问到第 10 个,如果遇到一个没带作业的,你就喊“有人没带作业!”然后停止检查(break)。如果全部检查完都没有人忘带,你就说“全班都带了!”(else 执行)。
代码实现:
students = ["小明", "小红", "小刚", "小丽", "小华"]
has_no_homework = True # 假设每个同学都带了?实际上我们用循环检查
# 模拟检查过程:只有小丽没带作业
homework_status = {
"小明": True,
"小红": True,
"小刚": True,
"小丽": False,
"小华": True
}
for student in students:
if not homework_status[student]: # 如果这个学生没带作业
print(f"{student} 没带作业!")
break
else:
print("全班都带了作业!")
输出结果:
小丽 没带作业!
(没有执行 else 部分,因为被 break 打断了)
如果把小丽也改成带了作业,那么循环自然结束,就会打印“全班都带了作业!”。
3. 判断质数的经典例子(现有内容扩充)
判断一个数是不是质数,可以用 else 配合 for 循环。如果循环内部没有发现任何能整除的数(break 没执行),那么 else 就会执行,说明这个数是质数。
# 判断 2 到 9 哪些是质数
for i in range(2, 10): # i 从 2 到 9
for j in range(2, i): # j 从 2 到 i-1
if i % j == 0: # 如果 i 能被 j 整除
print(f"{i} 不是质数")
break # 跳出内层循环
else:
print(f"{i} 是质数")
输出:
2 是质数
3 是质数
4 不是质数
5 是质数
6 不是质数
7 是质数
8 不是质数
9 不是质数
注意:else 是跟内层 for 对齐的,不是跟外层 for。它表示:内层循环如果完整跑完(没有 break),就说明 i 没有因数,是质数。
二、用标志变量优雅地跳出多重循环
有时候我们会遇到嵌套循环(循环里套循环),比如想要找一对数字 (x, y) 满足某个条件。当找到后,我们希望同时跳出内外两层循环。直接 break 只能跳出内层循环,外层还需要再判断一次才能跳出。解决办法有两种:
方法1:使用标志变量(flag)
设置一个变量 found,初始为 False,找到目标后就设为 True,然后在内层 break 后,在外层检查这个变量,再 break。
生活中的例子:你在抽屉里找钥匙,抽屉里又分好几个小格子。你一层一层翻,如果找到了,你就不需要再翻剩下的格子和抽屉了。这里的“找到”就是标志。
# 寻找两个正整数(都在 1-9 之间),它们的乘积等于 36
found = False # 标志变量,初始为 False(还没找到)
for x in range(1, 10):
for y in range(1, 10):
if x * y == 36:
print(f"找到组合:{x} x {y} = 36")
found = True # 标记已找到
break # 跳出内层循环
if found: # 如果已经找到了,就再跳出外层循环
break
方法2:把循环放在函数里,用 return
当我们把嵌套循环放入一个函数,只要找到目标就可以用 return 直接退出函数,比标志变量更简洁。
def find_product(target):
"""在 1-9 范围内找两个数乘积等于 target,打印并返回第一个找到的组合"""
for x in range(1, 10):
for y in range(1, 10):
if x * y == target:
print(f"找到组合:{x} x {y} = {target}")
return x, y # 直接退出函数
find_product(36)
常见错误:新手容易忘记在外层也写一个 break,导致虽然内层跳出了,但外层还在继续循环,结果打印出多个组合。使用函数 return 可以避免这个麻烦。
三、综合案例:猜数字增强版(包含异常处理和 continue)
我们已经有一个猜数字游戏:程序随机生成一个 1~100 的数字,玩家有 5 次机会。如果猜对了就用 break 结束;如果 5 次都没猜对,循环正常结束,else 块提示机会用完。
现在我们要让程序更健壮:如果用户输入的不是数字(比如输入了“abc”),程序要提示“请输入数字”,并且 这次不算次数(让用户重新输入)。这就要用到 try-except 捕获异常(处理输入错误)和 continue 跳过本次循环。
完整代码(带中文注释)
import random
# 生成一个 1 到 100 之间的随机整数
secret = random.randint(1, 100)
max_chances = 5 # 最多可以猜 5 次
chances_used = 0 # 已经用了多少次(可选统计)
print("我想到一个1到100之间的数字,你有", max_chances, "次机会。")
while chances_used < max_chances:
user_input = input(f"第 {chances_used+1} 次猜:")
try:
guess = int(user_input) # 尝试将输入转为整数
except ValueError: # 如果转换失败(输入不是数字)
print("请输入数字!这次不消耗次数。")
continue # 跳过本次循环,不增加次数
# 只有输入合法数字才消耗次数
chances_used += 1
if guess == secret:
print("恭喜你,猜对了!")
break # 猜对就结束
elif guess > secret:
print("大了")
else:
print("小了")
else:
# 如果 while 循环因为条件不满足(次数用尽)正常结束,
# 而且没有被 break 打断,就会执行 else
print(f"机会用完了,数字是 {secret}。")
注意:这里把 for 改成了 while,因为 continue 后不希望增加 chances_used,用 for 会不好控制。实际上用 while 配合手动计数更灵活。else 在 while 循环中同样有效。
四、常见错误与提醒
-
忘记
else是和for还是if对齐?
else是和最内层的for或while对齐的,不要习惯性地认为和if配对。例如判断质数时,else是跟内层for的,不是跟if的。 -
break只能跳出当前一层循环。如果想跳出多层,必须用标志变量或函数return。新手经常只在内层break,以为外层也会停下,结果得到多余输出。 -
continue会跳过本次循环的剩余代码,但不会影响循环的迭代次数。在for循环中,它会直接进入下一次迭代;在while循环中,它回到条件判断。如果while里没正确更新计数器,可能导致死循环。比如上面猜数字例子中,如果用for并且continue后依然attempt会自增,就不能实现“不消耗次数”的效果,所以我们改用了while。 -
else在循环中的作用和if-else完全不同,初学者容易混淆。只有在循环没有被break打断时,else才会执行。
五、完整示例:模拟自动售货机找零
我们来一个贴近生活的综合例子:自动售货机里有 1元、5元、10元三种硬币,需要给顾客找零。输入要找的金额(整数,比如 18 元),程序尝试用尽量少的硬币(贪心算法)输出组合。这里我们用嵌套循环和 break 控制。
# 自动售货机找零:用最少硬币组合(1元 5元 10元)
def change_money(target):
"""找到 1元 5元 10元 的组合,使硬币数量最少"""
found = False # 标志变量,是否找到解
# 先尝试多用大面值硬币(贪心思想)
for ten in range(target // 10, -1, -1): # 10元硬币最多 target/10 个,从多到少
for five in range(target // 5, -1, -1): # 5元硬币最多 target/5 个
one = target - (ten * 10 + five * 5)
if one >= 0: # 剩余金额用 1元补足
print(f"找零 {target} 元:10元 x {ten},5元 x {five},1元 x {one}")
found = True
break # 找到解就跳出内层循环
if found:
break # 跳出外层循环
change_money(18) # 调用函数
输出:
找零 18 元:10元 x 1,5元 x 1,1元 x 3
这个程序利用了嵌套循环和标志变量,从大面值开始尝试,找到第一个解(硬币数最少)后立即跳出所有循环。
六、相关知识点指引
- 要想在输入出错时重试,除了
try-except,还可以用while True循环加break实现“直到输入合法才继续”。 - 如果你想深入学习异常处理,可以看《Python 异常捕获与调试》。
- 关于循环嵌套的更多技巧,可以学习《二维列表与矩阵操作》。
- 如果你想用循环控制玩更复杂的游戏,比如“猜数字”加上难度选择、记录历史猜测,可以看《函数与模块化设计》。
掌握这些循环控制工具后,你就能写出更智能、更稳健的程序了——比如自动检查班级作业、猜数字游戏不怕乱按、自动售货机聪明找零。快去试试吧!
例题精讲
以下Python代码的输出是什么? for i in range(3): for j in range(3): if j == 1: break print(i, j)
在Python中,如果for循环正常结束(没有遇到break),则else子句中的代码会被执行。
下面的代码使用嵌套循环和continue语句,统计0到9中所有不包含数字3的两位数的个数(十位从1-9,个位0-9)。请补全代码。
count = 0
for i in range(1, 10):
if i == 3:
___
for j in range(10):
if j == 3:
___
count += 1
print(count)以下代码的输出结果是什么? x = 0 while x < 5: x += 1 if x == 3: continue print(x, end=' ') else: print("完成")
下面的代码使用for-else结构,在列表numbers中查找第一个大于10的数字,找到后打印该数字并退出循环;如果没找到,则打印"未找到"。请补全代码。
numbers = [3, 7, 9, 12, 5]
target = None
for num in numbers:
if num > 10:
target = num
___
else:
print("未找到")
if target is not None:
print("找到:", target)