6.5 while 循环语句
for 循环必须以可迭代对象作为被循环的对象,while 循环则不同,它是依据一定的条件进行循环,即只要满足某条件,循环体中的语句“总跑着”。
6.5.1 基本格式
while 循环语句的基本格式是:
代码语言:javascript复制while <expression>:
<statements>
如果 <expression>
的布尔值是 True
,则执行循环体内的语句块 <statements>
——依然是以缩进标记语句块。
>>> n = 3
>>> while n > 0:
... n -= 1
... print(n)
...
2
1
0
在这段代码中,n
的初始值是 3
,则 while
后的表达式 n > 0
的布尔值是 True
,于是执行下面的语句块。执行 n -= 1
后 n
的值为 2
(见 print(n)
结果),n > 0
的布尔值还是 True
,继续执行循环体中的语句块,直到 n = 0
,表达式 n > 0
的布尔值是 False
,停止循环。
再如:
代码语言:javascript复制>>> lst = ['java', 'python', 'julia']
>>> while lst:
... print(lst.pop())
...
julia
python
java
依然要判断 while
后面的表达式 lst
的布尔值,因为在上一行定义了该列表不为空,故其布尔值是 True
,于是执行循环体中的语句块。lst.pop()
的功能是删除列表中最后的成员并返回删除对象,直到列表为空,其布尔值为 False
,则终止循环。
如果有兴趣在交互模式中输入下面的代码,会发现一个“神奇”的现象:
代码语言:javascript复制>>> while 1:
... print("laoqi")
...
观察到了吗?不看到终端不停地打印 'laoqi'
,就很难体会到什么叫做“死循环”——“循环到死”,无法靠程序自身的控制终止循环,也称“无限循环”。要停止上述“死循环”,最简单的方法就是关掉终端窗口。
6.5.2 break 和 continue 语句
在 Python 关键词中,break
和 continue
两个关键词构成了两个语句:
- break 语句:终止循环,并跳转到循环语句下面的语句;
- continue 语句:跳过此语句后面的循环体中的其他语句,回到循环的开始,并评估是否满足循环条件。
这两个语句可以用于 while 循环,也可以用于 for 循环。以 while 循环为例,其作用效果如图6-5-1所示。
图6-5-1 brak 和 continue 语句
用 break 语句将前面“死循环”的程序改造如下:
代码语言:javascript复制#coding:utf-8
'''
whileloop.py
'''
n = 0
while 1:
n = 1
if n >= 3: # (1)
break
print('laoqi')
print('loop end') # (2)
执行效果:
代码语言:javascript复制% python whileloop.py
laoqi
laoqi
loop end
对照程序代码和执行结果,当满足注释(1)时,即执行其下的 break
,从而避免无限循环,并跳转到循环语句下面的注释(2)。
下面写一个“猜数游戏”的程序,要求:(1)计算机随机生成一个100以内的正整数;(2)用户通过键盘输入数字,猜测计算机所生成的随机数。注意,用户的输入次数不进行限制。
相较于前述写过的程序,现在这个要求比较复杂,算是一个“大型程序”了。不过,不论面对多么复杂的程序,都要将需求分析清楚——本程序需求已明确,再针对需求制定实现策略。
- 生成随机数:用标准库的 random 模块;
- 获得用户输入的数字:用
input()
函数; - 实现不限次数的操作:用 while 循环语句;
- 判断用户的输入是否等于生成的随机数:用 if 语句;
- 猜中了终止循环:用 break 语句。
接下来的工作,就是运用已经学过的知识,将上述策略编写成可执行的程序。建议读者先自己编写,而后与下面的示例代码进行对照,取长补短。此处的代码示例仅仅是一种实现方式,并不是标准答案。
代码语言:javascript复制#coding:utf-8
'''
guessnumber.py
'''
import random
number = random.randint(1,100)
guess = 0
while True:
num_input = input("please input one integer that is in 1~100:")
guess = 1
if not num_input.isdigit():
print("Please input interger.")
elif int(num_input) < 0 or int(num_input) >= 100:
print("The number should be in 1 to 100.")
else:
if number == int(num_input):
print(f"OK, you’ve done well.It is only {guess} times.")
break
elif number > int(num_input):
print("your number is smaller.")
else:
print("your number is bigger.")
执行此程序,想一想怎么猜?观察我的猜测过程:
代码语言:javascript复制% python guessnumber.py
please input one integer that is in 1~100:50
your number is bigger.
please input one integer that is in 1~100:25
your number is smaller.
please input one integer that is in 1~100:37
your number is smaller.
please input one integer that is in 1~100:44
your number is smaller.
please input one integer that is in 1~100:47
your number is bigger.
please input one integer that is in 1~100:46
your number is bigger.
please input one integer that is in 1~100:45
OK, you’ve done well.It is only 7 times.
这个猜的过程,其实运用了一种简单的算法——二分查找算法(Binary Search Algorithm)。当然,运气好了,随便输入一个就能猜中,“蒙着猜”毕竟耗费的时间不稳定,而运用二分查找算法,你可以估计出最坏的时间复杂度。
由图6-5-1可知,另外一个关键词发起的 continue 语句,会略过此后的语句,回到循环的初始判断行,例如:
代码语言:javascript复制#coding:utf-8
'''
whilecontinue.py
'''
a = 11
while a > 0:
a -= 1
if a % 2 == 0:
continue # (3)
print(a) # (4)
else:
print(a) # (5)
还是先看调试结果,再解释必要的部分。
代码语言:javascript复制% python whilecontinue.py
9
7
5
3
1
在 while 循环语句块内,当 a
是偶数时,执行注释(3)的 continue 语句,依据图6-5-1所示,略过其后的注释(4),即不打印偶数,然后转到 while 循环的开始,满足条件即再自减 1
,则 a
变为奇数,执行注释(5),将奇数打印出来。于是得到了上述只显示奇数的执行结果。
break 语句和 continue 语句不仅仅可以用在 while 循环中,也能用于 for 循环,其作用亦然。
代码语言:javascript复制>>> for i in range(10):
... if i % 2 == 0:
... continue
... print(i)
... else:
... print(i)
...
1
3
5
7
9
这是 continue 语句在 for 循环中的应用举例,读者可以对照前述 whilecontinue.py
中的程序理解。
>>> for i in range(1, 10):
... if i % 5 == 0:
... break
... else:
... print(i)
...
1
2
3
4
注意,上述代码中的 range(1, 10)
——如果是 rang(10)
会是什么结果?为什么?
6.5.3 else 分支
在6.3.1节的图6-3-1中,显示 for 循环有一个可选的 else 分支。对于 while 循环,也可以有此分支,语法格式为:
代码语言:javascript复制while <expression>:
statements
else:
additional_statements
下面就以 while 循环为例,理解 else 分支的作用。例如:
代码语言:javascript复制>>> n = 5
>>> while n > 0:
... n -= 1
... print(n)
... else:
... print("this is else")
...
4
3
2
1
0
this is else
对照程序和上面的语法格式,当 n
自减到 0
时,n > 0
的布尔值是 False
,即不再执行 while 循环,则开始执行 else 分支下的语句。这段程序中没有 break
中断循环——循环是寿终正寝的。不然,使用 break 语句让循环“夭折”,会如何?
>>> n = 5
>>> while n > 0:
... n -= 1
... print(n)
... if n == 2:
... break
... else:
... print('this is else')
...
4
3
2
在6.5.2节中学习 break 语句的时候,图6-5-1中显示,遇到 break
后就跳到 while 循环后面的语句。上述程序结果说明,如果 while 后面有 else 分支, break
也会跳过它。
通过上述对比,了解了 else 分支的作用,但是,另外一个问题就来了,如果写成:
代码语言:javascript复制while <expression>:
statements
additional_statements
与上面包含 else 分支的语法有什么区别?
通过含有 break
的程序,就能看出区别:如果不使用 else 分支,additional_statements
的语句会总被执行,哪怕循环“夭折”也会执行。
事实上, else 分支在 while 循环中并非必要,Python 发明者吉多·范罗索姆甚至想剔除 while 的 else 分支。
在 for 循环中,else 分支会有如何表现?看下面的示例:
代码语言:javascript复制#coding:utf-8
'''
filename: forelse.py
'''
nums = [60, 70, 30, 110, 90]
found = False
for n in nums:
if n > 100:
found = True
print("There is a number bigger than 100")
break
if not found: # (6)
print("Not found!")
判断列表 nums
中是否有大于 100
的整数,若有,则 found = True
,并执行 break
结束循环。那么,此时注释(6)的条件就不满足了。否则,如果列表 nums
中没有使 n > 100
成立的整数,则 found = Fasle
,注释(6)的条件语句就会执行。显然,程序中的 found
就相当于一个“开关”(常说的“flag”)。如果用 else 分支,则可以省掉这个“开关”。
nums = [60, 70, 30, 110, 90]
for n in nums:
if n > 100:
print("There is a number bigger than 100")
break
else:
print("Not found!")
之所以能省掉 found
,也是因为循环被 break
后,else 分支不再执行。若修改 nums
中的数值,使 if n > 100
语句不执行,当循环完毕,则执行 else 分支——读者可以自行测试。由此可见,else 分支不论在 while 循环还是 for 循环,执行它的条件都是一样的。