目录
有时,需要重复多次执行一系列语句,循环语句就提供这样的功能。Python中的循环语句,有for循环语句和while循环语句两种。
3.1 for 循环语句
for循环语句格式如下:
for 变量 in 序列:
语句组1
else:
语句组2
先依次对序列中的每个值执行语句组1。然后,再执行语句组2。语句组1被执行的次数称为循环的次数。大多数情况下其实不需要else和语句组2。语句组要缩进。序列可以是range(...),也可以是字符串、列表、元组、字典、集合。例如:
for i in range(4):
print(i,end = " ")
程序输出:
0 1 2 3
range(n)是一个序列,包含整数0,1,2,……,n−1,相当于一个不包含n在内的左闭右开的区间[0,n)。上面的for语句,i依次取range(4)这个区间里面的每个值(0,1,2,3)并输出。
range(0)是一个空序列。因此下面程序无输出:
for i in range(0):
print(i)
range(m,n)则对应于一个不包含n的左闭右开的区间[m,n):
for i in range(5,9):
print(i,end = " ")
#>>5 6 7 8
range(n,n)是一个空序列。总之,若n小于等于m,则range(m,n)是空序列。
range(m,n,s)表示一个序列,m是起点,n是终点,s称为“步长”。序列第一个元素是m,第二个是m+s,第三个是m+2×s……。但是终点n不取。如果m大于n且s是负数,则为从大往小取。
for i in range(0, 10, 3): #步长为3,即每3个元素取1个
print(i,end = " ")
#>>0 3 6 9
for i in range(-10, -100, -30):
print(i,end = " ")
#>>-10 -40 -70
程序输出:
0 3 6 9 -10 -40 -70
可以用for循环遍历列表和字符串,即依次访问列表中的每个元素,以及字符串中的每个字符:
a = [123, 'ok', 'pku', 'QQ']
for i in range(len(a)): #len(a)求列表a长度(元素个数)
print(i, a[i], end = ",")
#>>0 123,1 ok,2 pku,3 QQ,
print("") #输出换行
for i in a:
print(i,end = " ")
#>>123 ok pku QQ
print("")
for letter in 'Taobao':
print(letter, end="")
#>>Taobao
第2行:如果x是字符串、列表、元组、字典或集合,len(x)可以求x的长度,即元素个数。比如,len("abc")的值是3。
第5行:输出空串,等于换行。
第6、7行:i的值依次取a[0],a[1],……,a[3],输出每个i。因此这个循环输出如第8行所示。
第10行:letter依次取'Taobao'中的每个字符。
程序输出:
0 123,1 ok,2 pku,3 QQ,
123 ok pku QQ
Taobao
下面是一个带else的for循环的例子:
cities = ["Beijing","Chengdu","Wuhan","Tianjin"]
for city in cities:
print(city,end = ",")
else:
print("No break")
print("Done!")
程序输出:
Beijing,Chengdu,Wuhan,Tianjin,No break
Done!
可见,在遍历完序列cities中的每个取值以后,才会执行else分支里面的语句组。
可以用for循环连续输出26个小写字母:
for i in range(26):
print(chr(ord("a") + i),end="")
程序输出:
abcdefghijklmnopqrstuvwxyz
在Python中,每个字符都是长度为1的字符串,每个字符都有一个编码。字符的编码是整数。ord(x)用于求字符x的编码,chr(x)用于求编码为整数x的字符。字符"a"到"z"的编码是连续的,即"a"的编码加上1就是"b"的编码,再加1就得"c"的编码……
上面这段话提到的编码,都是指Unicode编码。编码有Unicode、UTF-8、ASCII、GBK等多种,在7.2节会详细解释。
i取值从0到25,因此ord("a")+i依次是"a","b","c",……,"z"的编码,chr(ord("a")+i)自然就依次是"a","b","c",……,"z"。
"A"到"Z","0"到"9"的编码也是连续的。下面程序的输出是0123456789:
for i in range(10):
print(chr(ord("0") + i),end="")
3.2 break 语句和 continue 语句
break语句可以用于从循环中跳出。例如:
cities = ["Beijing","Chengdu","Wuhan","Tianjin"]
for city in cities:
if city[0] == 'W':
break
print(city,end = ",")
else:
print("No break")
print("Done!")
程序输出:
Beijing,Chengdu,Done!
第4行:break导致循环语句立即结束,本次循环语句组中剩余的部分即第5句也不执行。因此,当city等于"Wuhan"时,不打印"Wuhan",for循环直接结束,连else部分都不会被执行。
continue语句可以立即结束本次循环,开始下一次循环:
for letter in 'Taobao':
if letter == 'o': #字母为o时跳过输出
continue #直接跳到下次循环
print(letter,end = "")
程序输出:
Taba
第3行:continue导致本次循环立即结束,即语句组里剩下的第4句不执行,直接开始下一次循环,即letter变为下一个字母。所以字母'o'没有被输出出来。
3.3 多重循环
循环可以嵌套,即循环的语句组里面还可以包含循环,形成多重循环,写法为:
for i in 序列1:
......
for j in 序列2:
语句组2
......
for i这个循环称为外重循环,for j这个循环称为内重循环。如果序列1是range(n),序列2是range(m),那么语句组2会被执行n×m次。
3.4 while 循环语句
while语句格式如下:
while 表达式 exp:
语句组1
else:
语句组2
while循环执行步骤如下。
第1步:判断exp是否为真,若为真,转第2步;若为假,转第3步。
第2步:执行语句组1,然后回到第1步。
第3步:执行语句组2。
第4步:while语句执行完毕,程序继续往下执行。
一般情况下都不需要写else,而写成如下形式:
while 表达式 exp:
语句组1
执行步骤如下。
第1步:判断exp是否为真,若为真,转第2步;若为假,转第3步。
第2步:执行语句组1,然后回到第1步。
第3步:while语句执行完毕,程序继续往下执行。
连续输出26个字母可以用while循环实现如下:
i = 0
while i < 26: #只要i<26就执行下面
print(chr(ord("a") + i),end="")"
i+=1
有时会用到下面这种while写法:
whileTrue:
……
if exp:
break
……
while True就意味着这个循环会不停执行。只有在exp为真,执行break的情况下才会终止循环。
continue语句同样适用于while循环。
下面的程序提示用户输入密码,密码不正确则提示不正确,然后再次要求输入密码。密码正确则提示成功,然后结束。密码是pku。
while (input("请输入密码:") != "pku"):
print("密码不正确!")
print("密码输入成功!")
程序运行结果可能如下:
请输入密码:bba↙
密码不正确!
请输入密码:std↙
密码不正确!
请输入密码:pku↙
密码输入成功!
3.5 异常处理
程序的运行时错误也称为异常(RuntimeError),会导致程序突然中止,并输出一些表明异常产生原因的信息。这些信息在不懂的人看来十分诡异。引发异常的原因多种多样,下标越界、不正确的转换、除法除数为0、把整数和字符串相加、要打开的文件不存在……Python提供了对异常进行处理的手段,使得程序即便发生异常,也不会中止,而是可以根据程序员的意图继续运行。
try......except语句用于进行异常处理,其基本写法是:
try:
语句组1
except Exception as e:
语句组2
执行语句组1。如果执行过程中没有产生异常,则不会执行语句组2。如果产生异常,程序并不会中止,而是立即从语句组1中跳出,去执行语句组2。例如:
try:
n = int(input())
print("hello")
a = 100/n
print(a)
except Exception as e:
print(e) #输出导致异常的原因的信息,不是必需的
print("error")
print("end")
程序执行结果可能如以下几种情况:
5↙
hello
20.0
end
输入5,没有异常发生,因此第7、8行不会执行。
0↙
hello
division by zero
error
end
输入0,在第4行除数为0导致异常,第5行不执行,跳到第7行继续执行。
“division by zero”是第7行输出的导致异常的原因。
abc↙
invalid literal for int() with base 10: 'abc'
error
end
输入abc,在第2行试图将字符串'abc'转换成整数的时候即发生异常,第3行到第5行都不会执行,程序跳至第7行继续执行。
有的时候,希望对发生的异常强硬地不加理会,程序继续运行就好,那么可以这么写:
try:
……
except Exception as e:
pass
pass是Python语句,表示什么都不做,可以用在任何地方。用在任何地方都没什么作用,就是占个位子而已。
另外,“except Exception as e:”也可以简写为“except:”。
OpenJudge上的有些题目,没告诉你有多少数据,输入数据也没有结束的标志(有的题目会说输入一行“end”表示数据结束),这种情况下就需要用异常处理来进行输入。
3.6 调试程序的方法
学到循环,程序就会略有一点复杂,出错概率大增,必须学会一些调试程序的方法。
运行程序时,出现RuntimeError是很常见的。此时PyCharm会有出错信息,要仔细看,出错信息会指出程序在第几行出错。如果用到各种库,那么出错信息可能就会有很多,一些错误发生在并非自己编写的.py文件里。耐心往下看,出错信息里一定会指出自己写的.py文件到底哪一行出错。然后查词典也要看明白出错信息说的啥。
导致RuntimeError的原因,通常是输入数据结束了还执行input()、列表下标越界、不合法的转换、数值和字符串相加等。1.12节有详细说明,请仔细阅读。
如果程序可以运行,但就是得不到想要的结果,那就是存在逻辑错误。PyCharm提供了单步执行程序进行调试的功能,但是作者认为这么做效率不高,不推荐。推荐的做法是到处加print语句:用print语句输出你关心的变量的值,看看其变化过程是否正确;在if语句的每个分支都加print语句,就能看清楚到底哪个分支被执行;在循环里面加print语句,就能看到循环执行的次数到底对不对—总之,在各种地方都可以加print语句来看这个地方有没有被执行到。在这些用于调试的print语句后面不妨用注释做个标记,比如“#debug”,这样便于在正式版本里找到它们并删除。
在OJ做题时,最烦恼的是在本机能通过样例数据,但是程序提交后却得到WrongAnswer。此时就要自己多做一些数据进行测试。对于多组数据的题目,测试的时候一定也要用多组数据,因为忘了在处理每组数据前初始化一些变量,是老手都容易犯的错误。做测试数据的时候,要特别注意特殊情况、边界情况,比如负数、0、1、长度为1的字符串,数据范围里的最大值、最小值等。若输入数据是整数,不要偷懒都用1位整数,应该用不同位数的整数;输入数据是字符串,就要用不等长的字符串—总之测试数据应该有多样性,覆盖尽可能多的情况。如果程序有多个分支,则应构造不同的测试数据,使得每个分支都执行到,不能有未经测试的分支。
上面这些测试方法,不仅适用于做OJ题,真实的软件开发测试也要这么做。
再次强调一点,在OJ做题,不要犯程序输出的大小写和题目要求不一致的错误。
Python的assert语句也有助于发现程序bug,其用法如下:
assert 条件,提示
如果条件满足,则assert语句什么都不做;如果条件不满足,assert语句会导致RuntimeError,出错信息里会包含提示。例如:
asserta != 0,"a等于0了,错!"
假定变量a前面已经有定义,且a的值无论如何不该为0。如果a的值为0,程序可能出错,也可能运气好暂时不出明显的错(这样就发现不了a变成0了),但程序说不定有隐患—那么上面的语句就能确保a为0时,程序一定出错,从而就可以去找出a变成0的原因。