个人总结难免疏漏,请多包涵。更多内容请查看原文。本文以及学习笔记系列仅用于个人学习、研究交流。
本文主要介绍while循环、for循环,分别介绍格式、示例,以及break、continue、pass、循环else的使用。for循环介绍了各种遍历方法,序列、元组赋值、序列赋值、嵌套循环、文件循环等。下一篇文章将介绍循环技巧包括range、zip、map等函数。
目录
while循环
while语句是Python语言中最通用的迭代结构。只要顶端测试一直计算到真值,就会重复执行一个语句块(通常有缩进)。
称为“循环”是因为控制权会持续返回到语句的开头部分,直到测试为假。当测试变为假时,控制权会传给while块后的语句。结果就是循环主体在顶端测试为真时会重复执行,而如果测试一开始就是假,主体就绝不会执行。
格式
while语句最完整的输写格式是:首行以及测试表达式、有一列或多列缩进语句的主体以及一个可选的else部分(控制权离开循环而又没有碰到break语句时会执行)。
Python会一直计算开头的测试,然后执行循环主体内的语句,直到测试返回假值为止。
示例
第一个例子,while循环内有一个print语句,就是一直打印信息。回想一下,True只是整数1的特殊版本,总是指布尔真值;因为测试一直为真,Python会一直执行主体,或直到停止执行为止。这种行为通常称为无限循环。
>>> while True:
print('ctrl + c to stop')
ctrl + c to stop
ctrl + c to stop
ctrl + c to stop
ctrl + c to stop
ctrl + c to stopTraceback (most recent call last): #不按ctrl +c 就是死循环
File "<pyshell#2>", line 2, in <module>
print('ctrl + c to stop')
KeyboardInterrupt
>>>
下个例子会不断切掉字符串第一个字符,直到字符串为空返回假为止。直接测试对象,是一种很典型的用法。
>>> x = 'life'
>>> while x:
print(x,end = ' ')
x = x[1:]
life ife fe e
>>>
注意:这里使用end=' '关键字参数,使所有输出都出现在同一行,之间用空格隔开。
下面的例子会从a的值向上计算到b的值(但不含b)。
>>> a = 0; b = 10
>>> while a < b:
print(a,end = ' ')
a += 1
0 1 2 3 4 5 6 7 8 9
>>>
Python并没有其他语言中所谓的"do until"循环语句。可以在循环主体底部以一个测试和break来实现类似的功能。
break、continue、pass、循环else
break和continue语句只有嵌套在循环中时才起作用。
在Python中:
break
跳出最近所在的循环(跳过整个循环语句)。
continue
跳到最近所在循环的开头处(来到循环的首行)。
pass
什么也不做,只是空占位语句。
循环else块
只有当循环正常离开时才会执行(也就是没有碰到break语句)。
循环格式
加入break和continue语句后,while循环的一般格式如下所示。
break和continue可以出现在while(或for)循环主体的任何地方,但通常会进一步嵌套在if语句中,根据某些条件来采取对应的操作。
break
break语句会立刻离开循环。碰到break时,位于其后的循环代码都不会执行。所以有时可以引入break来避免嵌套化。
例如,以下是简单的交互模式下的循环,通过input输入数据,用户在请求的name处输入"stop"时就结束。
>>> while True:
name = input('输入姓名:')
if name == 'stop': break
age = input('输入年龄:')
print('Hi', name, int(age) ** 2)
输入姓名:22
输入年龄:221
Hi 22 48841
输入姓名:h
输入年龄:22
Hi h 484
输入姓名:stop
>>>
continue
continue语句会立即跳到循环的顶端。另外偶尔也避免语句的嵌套。
下面的例子使用continue跳过奇数。这个程序代码会打印所有小于10并大于或等于0的偶数。
>>> x = 10
>>> while x:
x -= 1
if x % 2 != 0: continue
print(x,end = ' ')
8 6 4 2 0
>>>
因为continue会跳到循环的开头,所以不需要在if测试内放置print语句。
只有当continue不执行时,才会运行到print。有点类似其他语言中的"goto",Python没有goto语句,但因为continue让程序执行时实现跳跃。有关使用goto所面临的许多关于可读性和可维护性的警告都适用。
刚开始使用Python的时候,continue可以少用。
例如,如果print是位于if底下,这种看起来会更清楚:
>>> while x:
x -= 1
if x % 2 == 0:
print(x,end = ' ')
8 6 4 2 0
>>>
pass
pass语句是无运算的占位语句,当语法需要语句并且还没有任何实用的语句可写时,就可以使用它。通常用于为复合语句编写一个空的主体。
例如,如果想写个无限循环,每次迭代时什么也不做,就写个pass。
>>> while True: pass # ctrl+c 停止
Traceback (most recent call last):
File "<pyshell#32>", line 1, in <module>
while True: pass
KeyboardInterrupt
>>>
因为主体只是空语句,Python陷入了死循环。就语句而言,pass差不多就像对象中的None一样,表示什么也没有。
跟if语句一样,在冒号之后,只有当主体不是复合语句时才可以,while循环主体和首行处在同一行上。
pass语句是有意义的用处,现在可能不会遇到,后面会讲解。例如,忽略try语句所捕获的异常,以及定义带属性的空类对象,而该类实现的对象行为就像其他语言的结构和记录。
pass有时指的是“以后会填上”,只是暂时用于填充函数主体而已:
无法保持函数体为空而不产生语法错误,可以用pass替代。
也可以用...(三个连续的点号)代替,由于省略号自身什么也不做,这可以当作是pass语句的一种替代方案,尤其是对于随后填充的代码——这是Python的"TBD"(未确定内容)的一种:
省略号可以和语句头出现在同一行,并且,如果不需要具体类型的话,可以用来初始化变量名:
循环else
和循环else子句结合时,break语句通常可以忽略其他语言中所需的搜索状态标志位。
例如,下列程序搜索大于1的因子,来决定正整数y是否为质数。
>>> x = y // 2
>>> while x > 1:
if y % 2 == 0:
print(y, 'has factor', x)
break
x -=1
else:
print(y, 'is prime')
除了设置标志位在循环结束时进行测试外,也可以在找到因子时插入break。这样循环else分句可以视为只有当没有找到因子时才会执行。如果没碰到break,该数就是质数。
while循环中,如果首行的测试一开始就是假,循环主体从没有执行过,循环else分句就会执行。
在这个例子中,如果x一开始就小于或等于1(例如,如果y是2),你还是会得到"is prime"的信息。
严格的数学定义来讲,小于2的数字就不是质数了。这个程序碰上负数和没有小数位数的浮点数也会失败。
更多内容
循环else分句是Python特有的,循环else分句提供了常见的编写代码的明确语法:编写代码的结构,可以捕捉循环的“另一条”出路,而不通过设定和检查标志位或条件。
例如,假设要写个循环搜索列表的值,而且需要知道在离开循环后该值是否已找到,可能会用这种方式编写该任务。
在这里,对标志位进行初始化、设置以及稍后再进行测试,从而确认搜索是否成功。这是有效的Python程序,也的确可以运行。
然而这正是循环else分句所要处理的结构种类。以下是else的等效版本。
这个版本要更简洁一些。在循环末尾使用else(和while这个关键字垂直对齐)取代了if测试。
因为while主体内的break会离开循环并跳过else,因此可作为捕捉搜索失败的情况更为结构化的方式。
C语言的while循环赋值 VS Python
Python不允许赋值这类语句出现在应该是表达式出现的场合。也就是说,这种常见的C语言编码样式在Python中是行不通的:
C赋值运算会返回赋值后的值,但Python赋值语句只是语句,不是表达式。
如果需要类似的行为,至少有三种方式可以在Python while循环中达到相同的效果,而不用在循环测试中嵌入赋值语句。
可以配合break,把赋值语句移到循环主体中来。
或者把赋值语句移进循环中再配合测试。
或者把第一个赋值语句移出循环外。
三种编码样式中,有些人认为第一种是最缺少结构化的方式,但是,这似乎是最简单的,而且也是最常用的。
for循环
for循环在Python中是一个通用的序列迭代器:可以遍历任何有序的序列对象内的元素。for语句可用于字符串、列表、元组、其他内置可迭代对象以及之后我们能够通过类所创建的新对象。
格式
Python for循环的首行定义了一个赋值目标(或一些目标),以及遍历的对象。首行后面是需要重复的语句块(一般都有缩进)。
当Python运行for循环时,会逐个将序列对象中的元素赋值给目标,然后为每个元素执行循环主体。循环主体一般使用赋值的目标来引用序列中当前的元素,就好像那是遍历序列的游标。
for首行中用作赋值目标的变量名通常是for语句所在作用域中的变量(可能是新的)。
这个变量名没什么特别的,甚至可以在循环主体中修改,但是,当控制权再次回到循环顶端时,就会自动被设成序列中的下一个元素。
循环之后,这个变量一般都还是引用了最近所用过的元素,也就是序列中最后的元素,除非通过一个break语句退出了循环。
for语句也支持一个选用的else块,它的工作就像是在while循环中一样:如果循环离开时没有碰到break语句,就会执行(也就是序列所有元素都访问过了)。之前介绍过的break和continue语句也可用在for循环中,就像while循环那样。
for循环完整的格式如下:
示例
基本应用
for循环可以遍历任何一种序列对象。例如,把变量名x依次由左至右赋值给列表中三个元素的每一个,而print语句将会每个元素都执行一次。在print语句内(循环主体),变量名x引用的是列表中的当前元素。
下面的两个例子会计算列表中所有元素的和与积。
>>> sum1 = 0
>>> for x in [1,2,3,4]:
sum1 = sum1 + x
>>> sum1
10
>>> prod = 1
>>> for item in [1,2,3,4]: prod *= item
>>> prod
24
>>>
其他数据类型
任何序列都适用for循环,因它是通用的工具。例如,for循环可用于字符串和元组。
for循环甚至可以应用在一些根本不是序列的对象上,对于文件和字典也有效。
for循环中的元组赋值
如果迭代元组序列,循环目标本身实际上可以是目标元组。这只是元组解包的赋值运算的另一个例子而已。
for循环把序列对象元素赋值给目标,而赋值运算在任何地方工作起来都是相同的。
>>> for (a,b) in t1:
print(a,b)
1 2
3 4
5 6
>>>
在这里,第一次走过循环就像是编写(a,b)=(1,2),而第二次就像是编写(a,b)=(3,4),依次类推。
任何赋值目标在语法上都能用在for这个关键字之后。
这种形式通常和后面所介绍的zip调用一起使用,以实现并行遍历。在Python中,它通常还和SQL数据库一起使用,其中,查询结果表作为这里使用的列表这样的序列的序列而返回——外围的列表就是数据库表,嵌套的元组是表中的行,元组赋值和列对应。
for循环中的元组使得用items方法来遍历字典中的键和值变得很方便,而不必再遍历键并手动地索引以获取值:
for循环中的元组赋值并非一种特殊情况,单词for之后的任何赋值目标在语法上都是有效的。尽管一般总是在for循环中手动地赋值以解包:
>>> t1
[(1, 2), (3, 4), (5, 6)]
>>> for both in t1:
a,b = both #手动给a,b分别赋值
print(a,b)
1 2
3 4
5 6
>>>
在遍历序列的序列的时候(嵌套序列),循环头部的元组可以节省了额外的步骤。比如a,b = both,省略了再把嵌套序列拎出来赋值。
在一个for中,嵌套的结构也能够以这种方式自动解包:
>>> ((a,b),c) = ((1,2),3) #嵌套序列
>>> a,b,c
(1, 2, 3)
>>> for ((a,b),c) in [((1,2),3),((4,5),6)]: print(a,b,c)
1 2 3
4 5 6
>>>
在每次迭代上,for循环直接运行这种嵌套赋值(省略分别循环后赋值)。任何嵌套的序列结构都可以按照这种方式解包,因为序列赋值非常通用:
>>> for ((a,b),c) in [((1,2),3),['XY',6]]: print(a,b,c)
1 2 3
X Y 6
>>>
for循环扩展序列赋值
由于for循环中的循环变量可以是任何赋值目标,这里可以用扩展序列解包赋值语法,来提取序列中的序列的元素和部分。它在赋值语句中有效,自动地在for循环中有效。
考虑前面小节介绍的元组赋值形式。在每次迭代时,值的一个元组赋给了名称的一个元组,就像是一条简单的赋值语句一样:
由于一个序列可以赋值给一组更为通用的名称(其中有一个带有星号的名称收集多个元素),可以在for循环中使用同样的语法来提取嵌套的序列的部分:
>>> a,*b,c = (1,2,3,4)
>>> a,b,c
(1, [2, 3], 4)
>>> for (a,*b,c) in [(1,2,3,4),'life']:
print(a,b,c)
1 [2, 3] 4
l ['i', 'f'] e
>>>
实际上,这种方式可以用来从表示为嵌套序列的数据的行中选取多个列。
嵌套for循环
下一个例子是在for中示范循环else分句以及语句嵌套。考虑到对象列表(元素)以及键列表(测试),这段代码会在对象列表中搜索每个键,然后报告其搜索结果。常见初学者的错误示范:
>>> items = ['a',22,(3,7),2.11]
>>> tests = [(3,7),3.1415]
>>> for key in items:
for item in items:
if item == key:
print(key,'is found')
break
else:
print(key,'not found')
a is found
22 is found
(3, 7) is found
2.11 is found
因为这里的嵌套if会在找到相符结果时执行break,而循环else分句是认定如果来到此处,搜索就失败了。
这段代码执行时,同时有两个循环在运行:外层循环扫描键列表,而内层循环为每个键扫描元素列表。循环else分句的嵌套是很关键的,其缩进至和内层for循环首行相同的层次,所以是和内层循环相关联的(而不是if或外层for)。
这段代码执行时,同时有两个循环在运行:外层循环扫描键列表,而内层循环为每个键扫描元素列表。循环else分句的嵌套是很关键的,其缩进至和内层for循环首行相同的层次,所以是和内层循环相关联的(而不是if或外层for)。
采用in运算符测试成员关系,这个示例就会比较易于编写。因为in会隐性地扫描列表来找到匹配,因此可以取代内层循环。
>>> for key in tests:
if key in items:
print(key,'is found')
else:
print(key,'not found')
(3, 7) is found
3.1415 not found
>>>
下一个例子以for执行典型的数据结构任务:收集两个序列(字符串)中相同元素。这差不多是简单的集合交集的例程。在循环执行后,res引用的列表中包含seq1和seq2中找到的所有元素。
>>> seq1 = 'spam'
>>> seq2 = 'scam'
>>>
>>> res = []
>>> for x in seq1:
if x in seq2:
res.append(x)
>>> res
['s', 'a', 'm']
>>>
程序代码只能用在两个特定的变量上:seq1和seq2。如果这个循环可以通用化成为一种工具,可以使用多次,结果就会很棒。后面我们会接触到Python中的函数。
文件循环
一般来说,每当你需要重复一个运算或重复处理某件事的时候,循环就很方便。因为文件包含了许多字符和行,它们也是循环常见的典型使用案例之一。要把文件内容一次加载至字符串,可以调用read:
>>> file = open('test.txt','w')
>>> file.write('1,2,222\neeeee,wqqwe\nfgfaasdsa,\nssss')
35
>>> file.close()
>>> file = open('test.txt','r')
>>> print(file.read())
1,2,222
eeeee,wqqwe
fgfaasdsa,
ssss
>>>
要分块加载文件,通常要么是编写一个while循环,在文件结尾时使用break,要么写个for循环。要按字符读取时,下面的两种代码编写的方式都可行。
>>> while True: #第一种
char = file.read(1)
if not char: break
print(char)
>>> for char in open('test.txt').read(): #第二种
print(char)
这里的for也会处理每个字符,但是会一次把文件加载至内存。要以while循环按行或按块读取时,可以使用类似于下面的代码。
>>> file = open('test.txt')
>>> while True:
line = file.readline()
if not line: break
print(line,end = '')
1,2,222
eeeee,wqqwe
fgfaasdsa,
ssss
>>>
>>> file = open('test.txt','rb') #二进制读取
>>> while True:
chunk = file.read(10) #最多10个字节
if not chunk: break
print(chunk)
b'eeee,wqqwe'
b'\r\nfgfaasds'
b'a,\r\nssss'
>>>
通常是按照块读入二进制数据的。不过,逐行读取文本文件时,for循环是最易于编写以及执行最快的选择。
>>> for line in open('test.txt').readlines():
print(line,end = '')
1,2,222
eeeee,wqqwe
fgfaasdsa,
ssss
>>> for line in open('test.txt'):
print(line,end = '')
1,2,222
eeeee,wqqwe
fgfaasdsa,
ssss
>>>
文件readlines方法会一次把文件载入到行字符串的列表,这里的最后的例子则按照文件迭代器来自动在每次循环迭代的时候读入一行。
最后一个例子通常是文本文件的最佳选择——它除了简单,还对任意大小的文件都有效,并且不会一次把整个文件都载入到内存中。