Python 循环结构详解
在编写程序时,我们经常遇到需要重复执行某条指令或某些指令的场景。例如,我们需要每隔 1 秒钟在屏幕上输出一次'hello, world'并持续输出一个小时。如果手动写代码,就需要把这段代码写 3600 遍,这显然是不现实且低效的。
Python 循环结构用于重复执行指令,主要包括 for-in 循环和 while 循环。for-in 适用于已知次数场景,配合 range 函数使用,支持步长控制;while 适用于未知次数场景,基于条件判断。循环控制关键字包括 break 用于终止循环,continue 用于跳过当前轮次。嵌套循环可用于构建表格等复杂逻辑。常见应用实例包括素数判断、最大公约数计算及猜数字游戏。文章还涵盖了循环最佳实践与调试技巧,强调避免死循环、优化性能及合理选择循环类型的重要性。掌握这两种结构是解决实际问题基础。

在编写程序时,我们经常遇到需要重复执行某条指令或某些指令的场景。例如,我们需要每隔 1 秒钟在屏幕上输出一次'hello, world'并持续输出一个小时。如果手动写代码,就需要把这段代码写 3600 遍,这显然是不现实且低效的。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
import time
print('hello, world')
time.sleep(1)
说明:Python 内置
time模块的sleep函数可以实现程序的休眠,参数1表示休眠的秒数,可以使用int或float类型,例如0.05表示50毫秒。
为了应对上述场景,我们可以在 Python 程序中使用循环结构。所谓循环结构,就是程序中控制某条或某些指令重复执行的结构。有了这样的结构,刚才的代码就不需要写 3600 遍,而是写一遍然后放到循环结构中重复 3600 次。在 Python 语言中构造循环结构主要有两种做法,一种是 for-in 循环,另一种是 while 循环。
如果明确知道循环执行的次数,我们推荐使用 for-in 循环。例如上面说的那个重复 3600 次的场景,我们可以用下面的代码来实现。注意,被 for-in 循环控制的代码块也是通过缩进的方式来构造,这一点跟分支结构中构造代码块的做法是一样的。我们被 for-in 循环控制的代码块称为循环体,通常循环体中的语句会根据循环的设定被重复执行。
"""
每隔 1 秒输出一次'hello, world',持续 1 小时
"""
import time
for i in range(3600):
print('hello, world')
time.sleep(1)
需要说明的是,上面代码中的 range(3600) 可以构造出一个从 0 到 3599 的范围,当我们把这样一个范围放到 for-in 循环中,就可以通过前面的循环变量 i 依次取出从 0 到 3599 的整数,这就让 for-in 代码块中的语句可以重复 3600 次。当然,range 的用法非常灵活,下面的清单给出了使用 range 函数的例子:
range(101):可以用来产生 0 到 100 范围的整数,需要注意的是取不到 101。range(1, 101):可以用来产生 1 到 100 范围的整数,相当于是左闭右开的设定,即 [1, 101)。range(1, 101, 2):可以用来产生 1 到 100 的奇数,其中 2 是步长(跨度),即每次递增的值,101 取不到。range(100, 0, -2):可以用来产生 100 到 1 的偶数,其中 -2 是步长(跨度),即每次递减的值,0 取不到。大家可能已经注意到了,上面的输出和休眠操作都没有用到循环变量 i,对于不需要用到循环变量的 for-in 循环结构,按照 Python 的编程惯例,我们通常把循环变量命名为 _,修改后的代码如下所示。虽然结果没什么变化,但是这样写显得更专业。
"""
每隔 1 秒输出一次'hello, world',持续 1 小时
"""
import time
for _ in range(3600):
print('hello, world')
time.sleep(1)
上面的代码要执行一个小时,如果想提前结束程序,在 PyCharm 中可以点击运行窗口上的停止按钮。如果在命令提示符或终端中运行代码,可以使用组合键 ctrl+c 来终止程序。
下面,我们用 for-in 循环实现从 1 到 100 的整数求和,即 $\sum_{n=1}^{100}n$。
"""
从 1 到 100 的整数求和
"""
total = 0
for i in range(1, 101):
total += i
print(total)
上面的代码中,变量 total 的作用是保存累加的结果。在循环的过程中,循环变量 i 的值会从 1 一直取到 100。对于变量 i 的每个取值,我们都执行了 total += i,它相当于 total = total + i,这条语句实现了累加操作。所以,当循环结束,我们输出变量 total 的值,它的值就是从 1 累加到 100 的结果 5050。注意,print(total) 这条语句前是没有缩进的,它不受 for-in 循环的控制,不会重复执行。
我们再来写一个从 1 到 100 偶数求和的代码,如下所示。
"""
从 1 到 100 的偶数求和
"""
total = 0
for i in range(1, 101):
if i % 2 == 0:
total += i
print(total)
说明:上面的
for-in循环中我们使用了分支结构来判断循环变量i是不是偶数。
我们也可以修改 range 函数的参数,将起始值和跨度修改为 2,用更为简单的代码实现从 1 到 100 的偶数求和。
"""
从 1 到 100 的偶数求和
"""
total = 0
for i in range(2, 101, 2):
total += i
print(total)
当然,更为简单的办法是使用 Python 内置的 sum 函数求和,这样我们连循环结构都省掉了。
"""
从 1 到 100 的偶数求和
"""
print(sum(range(2, 101, 2)))
如果要构造循环结构但是又不能确定循环重复的次数,我们推荐使用 while 循环。while 循环通过布尔值或能产生布尔值的表达式来控制循环,当布尔值或表达式的值为 True 时,循环体(while 语句下方保持相同缩进的代码块)中的语句就会被重复执行,当表达式的值为 False 时,结束循环。
下面我们用 while 循环来实现从 1 到 100 的整数求和,代码如下所示。
"""
从 1 到 100 的整数求和
"""
total = 0
i = 1
while i <= 100:
total += i
i += 1
print(total)
相较于 for-in 循环,上面的代码我们在循环开始前增加了一个变量 i,我们使用这个变量来控制循环,所以 while 后面给出了 i <= 100 的条件。在 while 的循环体中,我们除了做累加,还需要让变量 i 的值递增,所以我们添加了 i += 1 这条语句,这样 i 的值就会依次取到 1、2、3、……,直到 101。当 i 变成 101 时,while 循环的条件不再成立,代码会离开 while 循环,此时我们输出变量 total 的值,它就是从 1 到 100 求和的结果 5050。
如果要实现从 1 到 100 的偶数求和,我们可以对上面的代码稍作修改。
"""
从 1 到 100 的偶数求和
"""
total = 0
i = 2
while i <= 100:
total += i
i += 2
print(total)
我们再来看一个极端的场景,把 while 循环的条件直接设置为布尔值 True,还是从 1 到 100 的偶数求和。
"""
从 1 到 100 的偶数求和
"""
total = 0
i = 2
while True:
total += i
i += 2
if i > 100:
break
print(total)
上面的代码中使用 while True 构造了一个条件恒成立的循环,也就意味着如果不做特殊处理,循环是不会结束的,这也就是常说的'死循环'。为了在 i 的值超过 100 后让循环停下来,我们使用了 break 关键字,它的作用是终止循环结构的执行。需要注意的是,break 只能终止它所在的那个循环,这一点在使用嵌套循环结构时需要引起注意,后面我们会讲到什么是嵌套的循环结构。
除了 break 之外,还有另一个在循环结构中可以使用的关键字 continue,它可以用来放弃本次循环后续的代码直接让循环进入下一轮,代码如下所示。
"""
从 1 到 100 的偶数求和
"""
total = 0
for i in range(1, 101):
if i % 2 != 0:
continue
total += i
print(total)
说明:上面的代码使用
continue关键字跳过了i是奇数的情况,只有在i是偶数的前提下,才会执行到total += i。
和分支结构一样,循环结构也是可以嵌套的,也就是说在循环结构中还可以构造循环结构。下面的例子演示了如何通过嵌套的循环来输出一个乘法口诀表(九九表)。
"""
打印乘法口诀表
"""
for i in range(1, 10):
for j in range(1, i + 1):
print(f'{i}×{j}={i * j}', end='\t')
print()
上面的代码中,for-in 循环的循环体中又用到了 for-in 循环,外面的循环用来控制产生 i 行的输出,而里面的循环则用来控制在一行中输出 j 列。显然,里面的 for-in 循环的输出就是乘法口诀表中的一整行。所以在里面的循环完成时,我们用了一个 print() 来实现换行的效果,最后的输出如下所示。
1×1=1
2×1=2 2×2=4
3×1=3 3×2=6 3×3=9
...
9×1=9 9×2=18 ...9×9=81
要求:输入一个大于 1 的正整数,判断它是不是素数。
提示:素数指的是只能被 1 和自身整除的大于 1 的整数。例如对于正整数
n,我们可以通过在2到n-1之间寻找有没有n的因子,来判断它到底是不是一个素数。当然,循环不用从2开始到n-1结束,因为对于大于 1 的正整数,因子应该都是成对出现的,所以循环到 $\sqrt{n}$ 就可以结束了。
"""
输入一个大于 1 的正整数判断它是不是素数
"""
num = int(input('请输入一个正整数:'))
end = int(num ** 0.5)
is_prime = True
for i in range(2, end + 1):
if num % i == 0:
is_prime = False
break
if is_prime:
print(f'{num}是素数')
else:
print(f'{num}不是素数')
说明:上面的代码中我们用了布尔型的变量
is_prime,我们先将它赋值为True,假设num是一个素数;接下来,我们在2到num ** 0.5的范围寻找num的因子,如果找到了num的因子,那么它一定不是素数,此时我们将is_prime赋值为False,同时使用break关键字终止循环结构;最后,我们根据is_prime的值是True还是False来给出不同的输出。
要求:输入两个大于 0 的正整数,求两个数的最大公约数。
提示:两个数的最大公约数是两个数的公共因子中最大的那个数。
"""
输入两个正整数计算它们的最大公约数
"""
x = int(input('x = '))
y = int(input('y = '))
for i in range(x, 0, -1):
if x % i == 0 and y % i == 0:
print(f'最大公约数:{i}')
break
说明:上面代码中
for-in循环的循环变量值是从大到小的,这样我们找到的能够同时整除x和y的因子i,就是x和y的最大公约数,此时我们用break终止循环。如果x和y互质,那么循环会执行到i变成1,因为1是所有正整数的因子,此时x和y的最大公约数就是1。
用上面代码的找最大公约数在执行效率是有问题的。假如 x 的值是 999999999998,y 的值是 999999999999,很显然两个数是互质的,最大公约数为 1。但是我们使用上面的代码,循环会重复 999999999998 次,这通常是难以接受的。我们可以使用欧几里得算法(辗转相除法)来找最大公约数,它能帮我们更快的得到想要的结果,代码如下所示。
"""
输入两个正整数计算它们的最大公约数
"""
x = int(input('x = '))
y = int(input('y = '))
while y % x != 0:
x, y = y % x, x
print(f'最大公约数:{x}')
说明:解决问题的方法和步骤可以称之为算法,对于同一个问题,我们可以设计出不同的算法,不同的算法在存储空间的占用和执行效率上都会存在差别,而这些差别就代表了算法的优劣。大家可以对比上面的两段代码,体会一下为什么我们说欧几里得算法是更好的选择。上面的代码中
x, y = y % x, x语句表示将y % x的值赋给x,将x原来的值赋给y。
要求:计算机出一个 1 到 100 之间的随机数,玩家输入自己猜的数字,计算机给出对应的提示信息'大一点'、'小一点'或'猜对了',如果玩家猜中了数字,计算机提示用户一共猜了多少次,游戏结束,否则游戏继续。
"""
猜数字小游戏
"""
import random
answer = random.randrange(1, 101)
counter = 0
while True:
counter += 1
num = int(input('请输入:'))
if num < answer:
print('大一点.')
elif num > answer:
print('小一点.')
else:
print('猜对了.')
break
print(f'你一共猜了{counter}次.')
说明:上面的代码使用
import random导入了 Python 标准库的random模块,该模块的randrange函数帮助我们生成了1到100范围的随机数。变量counter用来记录循环执行的次数,也就是用户一共做出了几次猜测,每循环一次counter的值都会加1。
在实际开发中,合理使用循环结构不仅能提高代码的可读性,还能显著提升程序的性能。以下是一些关于循环结构的最佳实践建议:
for 循环,未知次数时使用 while 循环。这有助于减少逻辑错误,使意图更清晰。while True 时,务必确保循环内部有退出机制(如 break)。否则,程序将无限运行,消耗系统资源甚至导致崩溃。在编写循环代码时,遇到问题是很常见的。以下是一些实用的调试技巧:
print 语句,输出关键变量的值,帮助追踪循环的执行路径和变量变化。range 函数时。学会了 Python 中的分支结构和循环结构,我们就可以解决很多实际的问题了。通过这节课的学习,大家应该已经知道了可以用 for 和 while 关键字来构造循环结构。如果事先知道循环结构重复的次数,我们通常使用 for 循环;如果循环结构的重复次数不能确定,可以用 while 循环。此外,我们可以在循环结构中使用 break 终止循环,也可以在循环结构中使用 continue 关键字让循环结构直接进入下一轮次。
掌握循环结构是成为合格程序员的重要一步。通过不断的练习和实际应用,你将能够灵活运用这些结构来解决各种复杂的编程挑战。建议在课后尝试修改上述示例代码,例如改变求和的范围、调整猜数字游戏的难度等,以加深理解。