4.1 顺序结构
在任何编程语言中最常见的程序结构就是顺序结构。顺序结构就是程序从上到下一行行地执行,中间没有任何判断和跳转。
如果Python程序的多行代码之间没有任何流程控制,则程序总是从上往下依次执行,排在前面的代码先执行,排在后面的代码后执行。这意味着如果没有流程控制,Python程序的语句是一个顺序执行流,从上向下依次执行每条语句。
4.2 if分支结构
if分支使用布尔表达式或者布尔值作为分支条件来进行分支控制。
Python的uf分支既可作为语句使用,也可作为表达式使用。Python的if语句有三种形式:
(1)第一种形式:
if expression :statements...
(2)第二种形式:
if expression :statements...
else:statements...
(3)第三种形式:
if expression :statements...
else if expression:if statements..:
#...//可以有零条或多条elif语句
else:statements...
在上面的if语句的三种形式中,第二种形式和第三种形式是相通的,如果第三种形式中的elif快不出现,则变成了第二种形式。
对于上面的if分支语句,执行过程是非常简单的——如果if条件为“真”,程序就会执行if条件后面的多条语句……如果前面所有条件都为“假”,程序就会执行else后的代码块(如果有)。
在上面的条件语句中,if expression:、elif expression:及else:后缩进的多行代码被称为代码块,一个代码快通常被当成一个整个来执行(除非在运行过程中遇到return、break、continue等关键字)因此这个代码快也被称为条件执行体。
Python是一门很“独特”的语言,它的代码块是提供缩进来标记的(大部分语言都是用花括号或end作为代码块的标记),具有相同缩进的多行代码属于同一个代码块。如果代码块莫名其妙地乱缩进,Python解释器会报错——前文说过,Python不是格式自由的语言。
提示:关于Python的“缩进”风格,喜欢它的人说这是一种乐趣;不喜欢它的人说这是一门需要游标卡尺(一种长度测量仪器)的语言——因为你需要使用游标卡尺去测量每行代码的缩进。当然,这是一句玩笑话。
s_age=input("请输入你的年龄:")
age=int(s_age)
if age>20:#只有当age>20时,下面整体缩进的代码块才会执行#整体缩进的语句是一个整体,要么一起执行,要么一起不执行print("您的年龄超过20岁了")print("20岁以上的热应该学会承担责任...")
再次重复:python不是格式自由的语言,因此你不能随心所欲地缩进,必须按Python语法要求缩进。
4.2.1 不要忘记缩进
代码块一定要缩进,否则就不是代码块。
s_age=input("请输入你的年龄:")
age=int(s_age)
if age>20:
print("您的年龄已经大于20岁了")
上面程序地if条件与下面地print位于同一条竖线上,这样在if条件下就没有受控制的代码块了。因此,上面的程序会报出错误:
File "……………………………………………………………", line 4print("您的年龄已经大于20岁了")^
IndentationError: expected an indented block after 'if' statement on line 3
注意:if条件后的条件执行体一定要缩进,只有缩进后的代码才算条件执行体。
那么代码块(条件执行体)到底要缩进多少?这个随意,你可以缩进1个空格、两个空格、3个空格……或1个Tab位,这都是符合语法要求的。但从编程习惯来看,Python通常建议缩进4个空格。
有些时候,Python解释器不会报错,但并不代表程序没有错误。
s_age=input("请输入你的年龄:")
age=int(s_age)
if age>20:print("您的年龄已经大于20岁了")
print("20岁以上的人应该学会承担责任...")
'''
请输入你的年龄:14
20岁以上的人应该学会承担责任...
'''
从上面过程可以看出,我们输入的年龄明明小于20,但运行结果还是会打印“20岁以上的人应该学会承担责任…”。这是因为这条print语句没有缩进。如果这行代码不缩进,那么Python就不会把这行代码当成条件执行体,他就不受if条件的控制,因此无论用户输入什么,print总会执行。
如果忘记正确地缩进,很可能导致程序地运行结果超出我们的预期。
#定义变量b,并为其赋值
b=5
if b>4:#如果b>4,则执行下面的条件执行体,只有一行代码作为代码块print("b大于4")
else:#否则,执行下面的条件执行体,只有一行代码作为代码块b-=1
#对于下面代码而言,他已经不再是条件执行体的一部分,因此总会执行
print("b不大于4")
"""
b大于4
b不大于4
"""
如果要让print(“b不大于4”)语句也处于else控制之下,则需要让这行代码也缩进4个空格。if、else、elif后的条件执行体必须使用相同的缩进的代码块,将这个代码块整体作为条件执行体。当if后后有多条语句作为多条件执行体时,如果忘记了缩进某一行代码,则会引起语法错误。
#定义变量c,并为其赋值
c=5
if c>4:#如果c>4,则执行下面的条件执行体,只有一行代码作为代码块c-=1
#下面十一行普通代码,不属于条件执行体
print("c大于4")
#此处的else将没有if语句,因此编译出错
else:#否则,执行下面的条件执行体,只有一行代码作为代码块print("c不大于4")
在上面代码中,因为if后的条件执行体的最后一条语句没有缩进,所以系统只把c-=1一行代码当成条件执行体,当c-=1语句执行结束后,if语句也就执行结束。后买你的print语句已经是一行普通的代码,不再属于条件执行体,从而导致else语句没有if语句,引发编译错误。
"""File "", line 9else:^^^^
SyntaxError: invalid syntax
"""
为了改正上面的代码,需要让print语句也缩进4个空格。
4.2.2 不要随意缩进
需要说明的是,虽然Python语法允许代码块随意缩进N个空格,但同一个代码块内的代码必须保持相同的缩进,不能一会缩进2个空格,一会缩进4个空格。
s_age=input("请输入你的年龄")
age=int (s_age)
if age>18:print("年龄已经大于20岁了")print("20岁以上的人应该学会承担责任")
因为上面是4个空格,下面是5个空格,因此会报出如下错误:
"""File "D:\李嘉\python\pythonProject\main.py", line 5print("20岁以上的人应该学会承担责任")
IndentationError: unexpected indent
"""
把代码改为如下形式:
s_age=input("请输入你的年龄")
age=int (s_age)
if age>18:print("年龄已经大于20岁了")print("20岁以上的人应该学会承担责任")
上面第二条print语句只缩进3个空格,前面的print语句缩进4个空格,同样不属于同一个代码块,因此Python解释器还是会报错:
"""File "", line 5print("20岁以上的人应该学会承担责任")^
IndentationError: unindent does not match any outer indentation level
"""
上面介绍,Python代码块中的所有语句必须保持相同的缩进,既不能多,也不能少。
注意:位于同一个代码块中的所有语句必须保持相同的所经,既不能多,也不能少。
另外,需要说明的是,对于不需要使用代码块的地方,千万不要随意缩进,否则程序会报错:
mas="Hello World"print(msg)
"""File "", line 2print(msg)
IndentationError: unexpected indent
"""
上面程序只有两条简单的执行语句,并没有分支、循环等流程控制,因此不应该使用缩进。
4.2.3 不要忘记冒号
从Python解释器的角度来看,Python冒号精确表示代码块的开始点——这个功能不仅在条件执行体中如此,后面的循环体、方法体、类体全部都遵守该规则。
如果遗忘冒号,那么Python会无法识别代码块的开始点。
age=24
if age>20print("年龄已经大于20岁了")print("20岁的人要学会承担责任...")
"""
File "", line 2
if age>20^
SyntaxError: expected ':'
"""
4.2.4 if条件的类型
从前面的失利可以看到,Python执行if语句时,会判断if条件是True还是False。那么if条件是不是只能使用bool类型的表达式呢?不是,if语句可以是任意类型,当下面的值作为bool表达式时,会被解释器当作False处理。
False、None、0、""、()、[]、{}
从上面的介绍可以看出,出=除False本身,各种代表“空”的None、空字符串、空元组、空表、空字典都会被当成False处理。
#定义空字符串
s=""
if s:print('s不是空字符串')
else:print('s是空字符串')
#定义空列表
my_list=[]
if my_list:print('my_list不是空列表')
else:print('my_list是空列表')
#定义空字典
my_dict={}
if my_dict:print('my_dict不是空字典')
else:print('my_dict是空字典')
4.2.5 if分支的逻辑错误
对于if分支,还有一个很容易出现的逻辑错误,这个逻辑错误并不属于语法问题,但引起错误的可能性更大。
age=45
if age>20:print("青年人")
elif age>40:print("中年人")
elif age>60:print("老年人")
从表面上看,上面的程序没有任何问题:人的年龄大于20岁是青年人,年龄大于40岁是中年人,年龄大于60岁是老年人。但结果是:青年人。而实际上希望45岁应该被判断为中年人——这显然出现了一个问题。
对于任何的if else语句,从表面上看,else后面没有任何条件,或者elif后只有一个条件——但这不是真相,因为else的含义是“否则”——else本身就是一个条件!这也是把if、else后的代码块统称为条件执行体的原因,else的隐含条件是对前面条件取反。因此,上面的代码实际上可以写成:
age=45
if age>20:print("青年人")
elif age>40 and not(age>20):print("中年人")
elif age>60 and noy(age>20) and not(age>40 and not age >20):print("老年人")
为了正确的目的,把程序改为:
age=45
if age>60:print("老年人")
elif age>40 :print("中年人")
elif age>20:print("青年人")
注意:在使用if else分支语句时,一定要处理包含范围更小的情形。
4.2.6 if表达式
正如前面所介绍的,if分支语句还可作为表达式,此时if表达式相当于其他语言中的三目运算符。
4.2.7 pass语句
很多程序都提供“空语句”支持,Python也不例外,Python的pass语句就是空语句。
有时候程序需要占一个位、放一条语句,但不希望这条语句做任何时,此时就可通过pass语句来实现。通过使用pass语句,可以让程序更加完整。
s=input("请输入一个整数:")
s=int(s)
if s>5:print("s大于5")
elif s<5:#空语句,相当于占位符pass
else:print("s等于5")
上面程序中,对于s小于5的情形,程序暂时不想处理(或不知道如何处理),此时程序就需要通过空语句来占一个位,这样即可使用pass语句了。
4.3 断言
断言语句和if分支优点类似,它用于对一个bool表达式进行断言,如果该bool表达式位True,改成西可以继续向下执行;否则程序会引发AssertionError错误。
s_age=input("请输入您的年龄:")age=int (s_age)assert 20< age< 80print("您输入的年龄在20和80之间")
上面程序中断言age必须位于20到80之间。运行上面的程序,有两种情况:
(1)"""请输入您的年龄:30您输入的年龄在20和80之间"""
(2)"""请输入您的年龄:10Traceback (most recent call last):File "D:\李嘉\python\pythonProject\main.py", line 3, in <module>assert 20< age< 80AssertionError"""
从上面的运行过程可以看出,断言也可以对逻辑表达式进行判断,因此实际上断言也相当于一种特殊的分支。
assert断言的执行逻辑是:
if 条件为False:程序会引发AssertError错误
4.4 循环结构
循环语句可以在满足循环条件的情况下,反复执行某一段代码,这段代码被重复执行的代码被称为循环体。当反复执行这个循环体时,需要在合适的时候把循环条件改为假,从而结束循环;否则循环将一致执行下去,形成死循环。循环语句可能包含四个部分:
(1)初始化语句:一条或多条语句,用于完成一些初始化工作。初始化语句在循环开始前执行。
(2)循环条件:这是一个布尔表达式,这个表达式能决定是否执行循环体。
(3)循环体:这个部分是循环的主体,如果循环条件允许,这个代码块将被重复执行。
(4)迭代语句:这个部分在一次循环体结束后,对循环体求值之前执行,通常用于控制循环条件中的变量,使得循环在合适的时候结束。
并不是每个循环中都有4个部分。
4.4.1 while循环
while循环的语法格式如下:
[init_statement]
while test_exprssion :body_statements[iteration_statements]
while循环在每次执行循环体之前,都要先对test_expression循环条件进行求值,如果循环条件为真,则运行循环体部分。从上面语法格式来看,迭代语句iteration_statements总是位于循环体的最后,因此只有当循环体能成功执行完成时,while循环才会执行迭代语句iteration_statements。
从这个意义上看,while循环也可被当成分支语句使用——如果test_expression条件以楷书就为假,则循环体部分同源不会获得执行的机会。
#循环的初始化条件
count_i=0
#当count_i小于10时,执行循环体
while count_i<10:print("count:",count_i)#迭代语句count_i+=1
print("循环结束")
在使用while循环时,一定要保证循环条件有变假的时候;否则这个循环将成为一个死循环,永远无法结束这个循环。
#下面是一个死循环
count_i2=0
while count_i2<10:print("不停执行的死循环:",count_i2)count_i2-=1
print("永远无法跳出的循环体")
在上面的代码中,count_i2的值越来越小,这将导致count_i2的值永远小于10,count_i2<10循环条件一直为True,从而导致这个循环永远无法结束。与前面介绍分支语句类似的是,while循环的循环体中所有代码必须使用相同的缩进,否则Python也会引发错误。
#循环体的初始化条件
count_i=0
#当count_i小于10时,执行循环体
while count_i <10:print("count_i的值:",count_i)
count_i+=1
运行上面的程序,将会看到一个死循环。这是由于count_i+=1代码没有缩进,这行代码不属于循环体。这样程序中的count_i将一直是0,从而导致count_i<10一直都是True,因此循环体就称为一个死循环。
4.4.2 使用while循环遍历列表和元组
由于列表和元组的元素都是有索引的,因此程序可通过while循环、列表或元组的元素的索引来遍历列表和元组中的所有元素。
a_tuple=('fkit','crazyit','Python')
i=0
#只对i小于len(a_tuple),继续执行循环体
while i<len(a_tuple):print(a_tuple[i])#根据i来访问i+=1
运行上面结果为:
"""
fkit
crazyit
Python
"""
按照上面的方法,while循环也可以用于遍历列表。
下面实现一个对一个整数列表的元素进行分类,能整除3的放入一个列表;除以3余1的放入另一个列表中;除以3余2的放入第三个列表中。
src_list=[12,45,34,13,100,24,56,74,109]
a_list=[]#定义保存整除3的元素
b_list=[]#定义保存除3余1的元素
c_list=[]#定义保存除3余2的元素
#只要src_list还要元素,就继续执行循环体。
while len(src_list)>0:#弹出src_list的最后一个元素ele=src_list.pop()#如果ele%3不等于0if ele % 3 == 0:a_list.append(ele)#添加元素elif ele % 3==1:b_list.append(ele)#添加元素else:c_list.append(ele)#添加元素
print("整除3:",a_list)
print("除3余1",b_list)
print("除3余2",c_list)
"""
整除3: [24, 45, 12]
除3余1 [109, 100, 13, 34]
除3余2 [74, 56]
"""
4.4.3 for-in循环
for-in循环专门用于遍历范围、列表、元组和字典等可迭代对象包含的元素。for-in循环的语法格式如下:
for 变量 in 字符串|范围|集合等:statements对于上面的语法
格式有两点说明:
- for-in循环中的变量受for-in循环控制,该变量将会在每次循环开始时自动被赋值,因此程序不应该在循环中对该变量赋值。
- for-in循环可用于遍历任何可迭代对象。所谓迭代对象,就是指该对象中包含一个__iter__方法,且该方法的返回值对象具有next()方法。
for-in循环可用于遍历范围。
s_max=input("请输入您想计算的阶乘")
mx=int(s_max)
result=1
#使用for-in循环遍历范围
for num in range(1,mx+1):result*=num
print(result)
上面程序将会根据用户输入的数字进行循环。假如用户输入7,此时程序将会构建一个range(1,8)对象(不包含8),因此for-in循环将会自动循环7次,在每次循环开始时,num都会被依次自动赋值为range所包含的每个元素。
for-in循环中的变量完全接受for-in循环控制,因此该变量也被称为循环计数器。
程序对for-in循环的循环技术器赋值在语法上时语序的,但没有什么意义,而且非常容易导致错误。
for i in range(1,5):i=20print('i:',i)
"""
i: 20
i: 20
i: 20
i: 20
"""
4.4.4 使用for-in循环遍历列表和元组
在使用for-in循环遍历列表和元组时,列表或元组有几个元素,for-in循环的循环体就执行几次,针对每个元素执行一次,循环计数器会依次被赋值为元素的值。
a_tuple=('crazyit','fkit','Python')
for ele in a_tuple:print('当前元素是',ele)
"""
当前元素是 crazyit
当前元素是 fkit
当前元素是 Python
"""
当然也可以按上面的方法来遍历列表。
例:计算列表所有数值元素的总和、平均值。
src_list=[12,45,3.4,13,'a',4,56,'crazyit',109.5]
my_sum=0
my_count=0
for ele in src_list:if isinstance(ele,int) or isinstance(ele,float):print(ele)#累加该元素my_sum+=ele#数值元素的个数加一my_count+=1
print("总和:",my_sum)
print("平均数:",my_sum/my_count)
"""
12
45
3.4
13
4
56
109.5
总和: 242.9
平均数: 34.7
"""
上面程序使用了isinstance()函数,该函数用于判断某个变量是否为指定类型的实例,其中前面一个参数是要判断的变量,后一个参数是类型。我们可以在Python的交互解释器中测试该函数的功能。
isinstance(2,int)#True
isinstance('a',int)#False
isinstance('a',str)#True
从上面的运行结果可以看出,使用isinstance()函数判断变量是否为指定类型非常方便、有效。
如果需要,for-in循环也可以根据索引来变量列表或元组;只要让循环计数器遍历0到列表长度的区间,即可提供该循环计数器来访问列表的元素。
a_list=[220,1.4,50,'fkit',-3.5]
#遍历0到len(a_list)的范围
for i in range(0,len(a_list)):#根据索引访问列表元素print("第%d个元素是:%s"%(i,a_list[i]))
"""
第0个元素是:220
第1个元素是:1.4
第2个元素是:50
第3个元素是:fkit
第4个元素是:-3.5
"""
4.4.5 使用for-in循环遍历字典
使用for-in循环遍历字典也是通过遍历普通列表来实现的。前面学字典一章中提到,字典包含了如下三种方法:
- items():返回字典中所有key-value对的列表
- keys():返回字典中所有key的列表。
- values():返回字典中所有value的列表。
因此,如果要遍历字典,完全可以先调用字典的上面三个方法之一来获取字典的所有key-value对、所有key、所有value,再进行遍历。
my_dict={'语文':89,'数学':92,'英语':80}
#通过items()方法辩论所有的key-value对
#由于items方法返回的列表元素是key-value对,因此要声明两个变量
for key,value in my_dict.items():print('key:',key,'value:',value)
print('-------------------------')
#通过keys()方法遍历所有的key
for key in my_dict.keys():print('key:',key)#再通过key获取valueprint('value:',my_dict[key])
print('---------------------------')
#通过values()方法遍历所有的value
for value in my_dict.values():print('value:',value)
"""
key: 语文 value: 89
key: 数学 value: 92
key: 英语 value: 80
-------------------------
key: 语文
value: 89
key: 数学
value: 92
key: 英语
value: 80
---------------------------
value: 89
value: 92
value: 80
"""
上面程序通过三个for-in循环分别遍历了字典的所有key-vlaue对、所有key、所有value。尤其是通过字典的items()遍历所有的key-value对时,由于items()方法返回的是字典中所有的key-value对组成的列表,列表元素都是长度为2的元组,因此程序要声明两个变量来分别代表key、value——这也是序列解包的应用。
假如需要实现一个程序,用于统计列表中各元素出现的次数。由于我们并不清楚列表中包含多少个元素,因此考虑定义一个字典,以列表的元素为key,该元素出现的次数为value,程序如下:
arc_list=[12,45,3.4,12,'fkit',45,3.4,'fkit',45,3.4]
statistics={}
for ele in arc_list:#如果字典中包含ele代表的keyif ele in statistics:#如果字典中包含ele代表的出现次数加1statistics[ele]+=1#如果字典中不包含ele代表的key,说明元素从未出现过else:#将ele元素代表的出现次数设为1statistics[ele]=1
#遍历dict打印各元素出现的次数
for ele,value in statistics.items():print("%s出现的次数有:%d"%(ele,value))
"""
12出现的次数有:2
45出现的次数有:3
3.4出现的次数有:3
fkit出现的次数有:2
"""
4.4.6 循环使用else
python的循环都可以定义else代码块,当循环条件为False时,程序会执行else代码块。
count_i=0
while count_i<5:print('count_i小于5:',count_i)count_i+=1
else:print("count_i大于或等于5:",count_i)
"""
count_i小于5: 0
count_i小于5: 1
count_i小于5: 2
count_i小于5: 3
count_i小于5: 4
count_i大于或等于5: 5
"""
从上面的运行过程来看,当循环条件count_i变成False时,程序执行了while循环的else代码块。
简单来说,程序再结束循环之前,先会执行else代码块。从这个角度来看,else代码块其实没有太大的价值——将else代码块直接放在循环体外即可。也就是说,上面的循环其实可以改为:
count_i=0
while count_i<5:print('count_i小于5:',count_i)count_i+=1
print("count_i大于或等于5:",count_i)
"""
count_i小于5: 0
count_i小于5: 1
count_i小于5: 2
count_i小于5: 3
count_i小于5: 4
count_i大于或等于5: 5
"""
上面代码直接将else代码块放在while循环体外,程序执行结果与使用else代码块的执行结果完全相同。
循环的else代码块时Python的一个很特殊的用法(其他编程语言通常不支持),else代码块的主要作用是便于生成更优雅的Python代码。
for循环同样可使用else代码块,当for循环把区间、元组或列表的所有元素遍历依次之后,for循环会执行else代码块,在else代码块中,循环计数器的值依然等于最后一个元素的值。
a_list=[330,1.4,50,'fkit',-3.5]
for ele in a_list:print("元素:",ele)
else:#访问循环计数器的值,依然等于最后一个元素的值print('else块:',ele)"""
元素: 330
元素: 1.4
元素: 50
元素: fkit
元素: -3.5
else块: -3.5
"""
4.4.7 嵌套循环
如果把一个循环放在另外一个循环体内,那么就么就可以形成嵌套循环。嵌套循环既可以是for-in循环嵌套while循环,也可以是while循环嵌套do while循环……即各种类型的循环都可以作为外层循环,各种类型的循环也可以作为内层循环。
当程序遇到嵌套循环时,如果外层循环的循环条件允许,则开始执行外层循环的循环体,而内层循环将被外层循环的循环体来执行——只是内层循环需要反复执行自己的循环体而已。当内层循环执行结束且外层循环的循环体也执行结束之后,将再次计算外层循环的条件,决定是否再次开始执行外层循环的循环体。
根据上面分析,假设外层循环的次数为n次,内层循环的次数是m次,那么内层循环的循环体实际上需要执行n×m次。
嵌套循环就是把内层循环当成外层循环的循环体。只有内层循环的条件为假的时候,才会完全跳出内层循环,才可以结束外层循环的当此循环,开始下一次循环。
#外层循环
for i in range(0,5):j=0#内层循环while j<3:print("i的值为:%d,j的值为:%d"%(i,j))j+=1
"""
i的值为:0,j的值为:0
i的值为:0,j的值为:1
i的值为:0,j的值为:2
i的值为:1,j的值为:0
i的值为:1,j的值为:1
i的值为:1,j的值为:2
i的值为:2,j的值为:0
i的值为:2,j的值为:1
i的值为:2,j的值为:2
i的值为:3,j的值为:0
i的值为:3,j的值为:1
i的值为:3,j的值为:2
i的值为:4,j的值为:0
i的值为:4,j的值为:1
i的值为:4,j的值为:2
"""
实际上,嵌套循环不仅可以是两层循环,还可以是三层、四层……不论循环如何嵌套,都可以把内层循环当成外层循环的循环体来对待,区别只是这个循环体中包含了需要反复执行的代码。
4.4.8 for表达式
for表达式用于利用其他区间、元组、列表等可迭代对象创建新的列表。for表达式的语法如下:
[表达式 for 循环计数器 in 可迭代对象]
从上面的语法格式可以看出,for表达式与普通for循环的区别有两点:
- 在for关键字之前定义一个表达式,该表达式通常会包含循环计数器。
- for表达式没有循环体,因此不需要冒号
for表达式当然也是有循环的,它同样会对可迭代对象进行循环——可迭代对象包含几个对象,该循环就对for之前的“表达式”执行几次(相当于for之前的表达式就是循环体),并将每次执行的值收集起来作为新的列表元素。
for表达式最终返回的是列表,因此for表达式也被称为列表推导式。
a_range=range(10)
#对a_range执行for表达式
a_list=[x*x for x in a_range]
#a_list集合包含10个元素
print(a_list)#[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
还可以在for表达式后面加上if条件,这样for表达式将指迭代那些符合条件的元素。
b_list=[x*x for x in a_range if x%2==0]
#a_list集合包含5个元素
print(b_list)#[0, 4, 16, 36, 64]
如果将for表达式的方括号改为圆括号,for表达式将不再生成列表,而是生成一个生成器(generator),该生成器同样可使用for循环迭代。
对于使用圆括号的for表达式,它最终返回的是生成器,因此这种for表达式也被称为生成器推导式。
#使用for表达式创建生成器
c_generator=(x*x for x in a_range if x%2==0)
#使用for循环迭代生成器
for i in c_generator:print(i,end='\t')#0 4 16 36 64
print()
在前面看到的for表达式都只有一个循环,实际上for循环表达式可使用多个循环,就像嵌套循环一样。
d_list=[(x,y)for x in range(5) for y in range(4)]
#d_list列表包含20个元素
print(d_list)#[(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3), (3, 0), (3, 1), (3, 2), (3, 3), (4, 0), (4, 1), (4, 2), (4, 3)]
上面的for表达式相当于如下的嵌套循环。
dd_list=[]
for x in range(5):for y in range(4):dd_list.append((x,y))
当然也支持类似于三层嵌套的for表达式。
e_list=[[x,y,z] for x in range(5) for y in range(4) for z in range(6)]
#e_list列表包含120个元素
print(e_list)
对于包含多个循环的for表达式,同样可指定if条件。假如我么有一个需求:程序要将两个列表中的数值按“能否整除”关系配对在一起。
src_a=[30,12,66,34,39,78,36,57,121]
src_b=[3,5,7,11]
#只要y能整除x,就将他们配对在一起
result=[(x,y) for x in src_b for y in src_a if y%x==0]
print(result)#[(3, 30), (3, 12), (3, 66), (3, 39), (3, 78), (3, 36), (3, 57), (5, 30), (11, 66), (11, 121)]
4.4.9 常用工具函数
使用zip()函数可以把两个列表“压缩”成一个zip对象(可迭代对象),这样就可以使用一个循环并行遍历两个列表。为了测试zip()函数的功能,我们可以在交互解释器中“试验”一下该函数的功能。
从上面测试的结果来看,zip()函数压缩得到的可迭代对象所包含的元素是由原列表元素组成的元组。
从上面代码可以看出,如果zip()函数压缩的两个列表长度不相等,那么zip()函数将以长度更短的列表为准。
zip()函数不仅可以压缩两个列表,还可以压缩多个列表。
从上面的代码可以看出,如果使用zip()函数压缩N个列表,那么zip()函数返回的可迭代对象的元素就是长度为N的元组。
books=['crazyit','fkit','Python']
prices=[79,64,89]
#使用zip函数压缩两个列表,从而实现并行遍历
for books,price in zip(books,prices):print(books,price)
"""
crazyit 79
fkit 64
Python 89
"""
有些时候,程序需要进行反向遍历,此时可通过reversed()函数,该函数可接收各种序列参数,然后返回一个“反序排列”的迭代器,该函数对参数本身不会产生任何影响。在交互解释器中,测试该函数的过程:
从上面代码可以看出,通过reversed()函数得到了range(10)的反转序列;但如果再次访问a,将会看到a并没有发生变化。
reversed()当然也可以对列表、元组进行反转。
前面提到过,str其实也是序列,因此也可以通过该函数实现在不影响字符串本身的前提下,对字符串进行反序遍历。
与reversed()函数类似的还有sorted()函数,该函数接收一个可迭代的对象作为参数,返回一个对元素排序的列表。
在交互解释器中测试该函数:
从上面的运行过程可以看出,sorted()函数也不会改变所传入的可迭代对象,该函数只是返回一个新的、排好序的列表。
在使用sorted函数中,还可以传入一个reverse参数,如果将该函数设置为True,则表示反向排序。
在调用sorted()函数时,还可以传入一个key参数,该参数可指定一个函数来生成排序的关键值。比如希望根据字符串的长度排序,则可为key赋值为len函数。
通过sorted()函数的帮助,程序可对可迭代对象按照从小到大的顺序进行遍历。
b=['fkit','crazyit','Charlie','fox','Emily']
for s in sorted(b,key=len):print(s)
"""
fox
fkit
Emily
crazyit
Charlie
"""
4.5 控制循环结构
Python语言没有提供goto语句来控制程序的跳转,这种做法虽然提高了程序流程控制的可读性,但降低了灵活性。为了弥补这种不足,Python提供了continue和break来控制循环结构。除此之外,使用return可以结束整个方法,当然也结束一次循环。
4.5.1 使用break结束循环
某些时候,需要在某种条件出现时强行终止循环,而不是等到循环条件为False时才退出循环。此时,可以使用break来完成这个功能。break用于完全结束一个循环,跳出循环体。不管是哪种循环,一旦在循环体中遇到break,系统就将完全借宿该循环,开始执行循环之后的代码。
#一个简单的for循环
for i in range(0,10):print('i的值为:',i)if i==2:#执行该语句时将结束循环break
"""
i的值为: 0
i的值为: 1
i的值为: 2
"""
运行上面的代码,将看到i循环到2时即结束,因为当i等于2时,再循环体内遇到了break语句,程序跳出该循环。
对于带else块的for循环,如果使用break强行终止循环,程序将不会执行else块。
#一个简单的for循环
for i in range(0,10):print('i的值为:',i)if i==2:#执行该语句时将结束循环break
else:print('else块:',i)
"""
i的值为: 0
i的值为: 1
i的值为: 2
"""
上面程序同样会在i等于2时跳出循环,而且此时for循环不会执行else块。在使用break语句的情况下,循环的else代码块与直接放入循环体后是有区别的——如果将代码块放在else块中,当程序使用break终止循环时,循环不会执行else块;如果将代码块直接放在循环体后面,当程序使用break中止循环时,程序自然会执行循环体之后的代码块。
Python的break语句不能像其他语言一样使用标签,因此它只可以结束其所在的循环,不可以结束嵌套循环的外层循环。
为了使用break语句跳出外层循环,可先定义bool类型的变量来标志是否需要跳出外层循环,然后再内层循环、外层循环中分别使用两天break语句来实现。
exit_flag=False
#外层循环
for i in range(0,5):#内层循环for j in range(0,3):print("i的值为:%d,j的值为:%d"%(i,j))if j==1:exit_flag=True#跳出内层循环break#如果exit_flag为True,跳出外层循环if exit_flag==True:break
"""
i的值为:0,j的值为:0
i的值为:0,j的值为:1
"""
上面程序在内层循环中判断j是否等于1,当j等于1时,程序将exit_flag设为True,并跳出内层循环;接下来程序开始执行外层循环的剩下语句,由于exit_flag为True,因此也会执行外层循环的break语句来跳出循环。
4.5.2 使用continue忽略本次循环的剩下语句
continue的功能和break有点类似,区别是continue只是忽略档次循环的剩下语句,接着开始下一次循环,并不会终止循环;而break则是完全中止循环本身。
#外层循环
for i in range(0,3):print('i的值是:',i)if i==1:#忽略当此循环的剩下语句continueprint("continue后的输出语句")
"""
i的值是: 0
continue后的输出语句
i的值是: 1
i的值是: 2
continue后的输出语句
"""
4.5.3 使用return结束方法
return用于从包围它的最直接方法、函数或匿名函数返回。当函数或方法执行一条return语句时(在return关键字后还可以跟变量、常量和表达式),这个函数或方法将被结束。
Python程序中的大部分循环都被放在函数或方法中执行,一旦在循环体内执行到一条return语句时,return语句就会结束该函数或方法,循环自认也随之结束。
def test():#外层循环for i in range(10):for j in range(10):print("i的值为:%d,j的值为:%d"%(i,j))if j==1:returnprint("return后的输出语句")
test()
"""
i的值为:0,j的值为:0
return后的输出语句
i的值为:0,j的值为:1
"""
提示:上面代码提供函数语法来定义函数。