第5章程序流控制与异常处理
本章主要介绍程序流的逻辑控制,以及程序流发生异常的处理方法。
本章的学习目标: 
 掌握if、while、for程序流控制流程; 
 掌握异常的捕获try…except…finally的使用。
5.1Python程序控制流
Python源程序代码是根据一定的逻辑条件运行的程序流,程序流包括顺序语句、条件语句和循环语句。顺序语句按照源程序代码的先后顺序,从上到下依次运行每一条程序语句。顺序结构是程序的最基本结构(见图5.1)。


图5.1顺序结构


条件语句(if)和循环语句(while、for)是根据一定的逻辑条件(True或者False)来选择后续运行的代码块。
提示:  Python无switch或case语句。因为按照Python之“禅(Zen)”宗旨,该语句属于多余。
5.2if条件语句
Python语言中if条件语句是最基本的逻辑判断程序流控制语句。语句的关键词为if…elif…else,关键字elif是else if的缩写。其格式为:  



ifC1:

程序代码1

elif C2:

程序代码2

else:

程序代码3



if条件语句的语法说明: 
(1)  if条件后面用冒号“:”表示满足条件后要运行的语句块。
(2) if条件语句可以有0~n(任意)个elif,而不用else if。
(3) if条件语句结束可以有0~1个else语句。
(4) if语句可以嵌套,使用缩进空格数来区分嵌套语句块。




if条件语句的程序流程图如图5.2所示。


图5.2if条件语句的程序流程图


图5.2中的if条件语句程序运行流程如下: 
(1)  如果C1为True,将运行“程序代码1”块语句。
(2) 如果C1为False,将判断C2。
(3) 如果C2为True,将运行“程序代码2”块语句。
(4) 如果C2为False(else),将运行“程序代码3”块语句。
【例5.1】输入0~100任意成绩数据,通过if条件语句判断成绩的等级。



num = int(input('请输入分数: '))

if0<= num < 60:# 判断值是否在0~60

print ('挂科!')

elif 60<=num<70:# 判断值是否在60~70





print ('及格')

elif 70<=num<80:# 判断值是否在70~80

print ('中')

elif 80<=num<90:# 判断值是否在80~90

print ('良好')

elif 80<=num<=100:# 判断值是否在90~100

print ('优秀')

else:

print ('输入有误!')



提示:  程序如果只需要条件赋值,可以用if的简写形式。
如果字符串s的长度大于4,把s赋值给b,否则输出'too short'。



s= input('请输入一个长度大于4的字符串: ')

b=s if len(s)>4 else 'too short'



如果a等于10,输出'等于10',否则输出'不等于10'。



a=23

print('等于10') if a ==10 else print('不等于10')



5.3Python循环语句
Python语言中有两种类型的循环语句,即while循环语句和for 循环语句。
提示:   Python无do…while循环语句。因为按照Python之“禅(Zen)”,它属于多余的。
5.3.1while循环语句
运行while循环语句,程序先判断循环条件,如果条件满足,则进入再循环。

while循环语句的语法说明: 
(1)  while循环语句后面用冒号“:”表示满足条件后要运行的语句块。
(2) while循环语句可以嵌套,使用缩进空格数来区分嵌套语句块。
(3) while循环语句可以有else(很少用)语句。
【例5.2】while循环语句示例。



# 用while循环语句,必须有一个控制逻辑条件

count=0

while count<3:

# 循环就是重复运行循环体里面的代码

print(f'Round.{count} test!')

count+=1



运行结果: 



Round.0 test!

Round.1 test!

Round.2 test!



5.3.2for…in循环语句
Python中的for…in 语句是另一种循环语句,该语句必须有一个可迭代(iterates)的对象才能循环。它会遍历序列(可迭代对象)中的每一个元素。
提示:  Python的for语句是遍历任何可迭代的对象,如list、set等,而不是仅用来控制循环次数或循环条件,这与C和Java语言中的for语句有本质不同。



a = ['hello', 'world']

for i in range(len(a)):

print(i, a[i])



运行结果: 



0 hello

1 world



while和for循环语句可以包含else子句(虽然使用概率很低),即结束while或for循环后运行的语句。
【例5.3】for循环代码示例。



'''

for 迭代对象 in 序列:

代码块(一行语句或多行代码)

else:

代码块(一行语句或多行代码)

'''

while 条件: 

else:

代码块(一行语句或多行代码)

'''



示例代码: 



sites = ["Youtub", "Google","Baidu","Taobao"]

for site in sites:

if site == "sohu":

break

print("get " + site)





else:

print("没有循环数据!")

print("完成循环!")



5.3.3break、continue、pass语句


图5.3循环与continue、

break控制语句


break语句与Java和C语言的break控制语句一样。即程序运行到break语句,中断循环、跳出break所在的最里层的循环体。
continue语句与Java和C语言的continue控制语句一样。即程序运行到continue,不再向下运行,直接再进入下一次循环。
关于continue和break语句的区别(见图5.3),可以举一个通俗的例子场景来表达。

 使用continue语句的场景: 甲乙两人计划准备下5局象棋,两人在下到第3局中途时,乙看到本局败势已定,但乙并不服输,于是放弃第3局,而接着比赛下面第4、5局,这时使用continue语句。
 使用break语句的场景: 甲乙两人计划下5局象棋,两人在下到第3局中途,乙看到败局已定,丧失了信心,直接放弃所有比赛,退出比赛,这时使用break语句。

【例5.4】continue语句代码示例。



# continue语句,跳过循环

a = '2123456'

for letter in a:

if letter == '2':

continue

print(letter)

#运行结果: 1 3 4 5 6



break语句用来终止循环语句,即循环条件没有False条件或者序列还没被完全递归完,也会停止运行循环语句。
【例5.5】break语句代码示例。



# break语句,跳出循环

b = '1234567'

for sam in b:

if sam == '2':

break

print(sam)

#运行结果: 1



pass语句是空语句,不运行任何操作。通常在开发程序的原型或调试程序时,用它来占位,将来用代码替换掉pass语句。
如: 



while 条件: 

pass



5.4异常

异常是一种事件,
它有可能在程序运行过程中发生,会改变程序的正常运行。Python程序运行时,可能
涉及输入输出、数学运算,以及对文件和网络资源的访问等。有时程序运行时,会遇到算
术运算除数为0、无权访问文件、网络连接中断等问题,程序会引发异常。必须在程序中捕获、处理这些异常,才能确保程序正确运行。
【例5.6】异常代码示例。



num=input('please input the number?')

for i in range(int(num)):

print(i)



上述程序在运行时,如果用户输入的是数字字符串,则可以得到正确结果,如果输入的是非数字,则程序会引发异常。



please input the number?ok

ValueError: invalid literal for int() with base 10: 'ok'



5.4.1异常的处理
异常处理是通过使用try…except…finally语句来捕获、处理异常实现的。其语法为: 



try:

<语句># 运行的代码

except <名字>: 

<语句># 如果在try代码块中,引发了'名字'异常

except <名字> as <数据>:

<语句># 如果引发了'名字'异常,获得附加的数据

finally:

<语句># 有没有异常,始终运行



1. 程序流程
(1) 运行try语句后,Python也在当前程序的上下文中做标记,这样当异常出现时就可以回到这里。如果运行try语句没有发生异常,则程序直接跳转到finally语句位置。
(2) 如果运行try后的语句发生异常,程序流就跳回到try语句,并运行第一个匹配该异常的except子句; 若没有该异常匹配的except子句,该异常将被递交到上层的try语句,或者到程序的最上层(这样将结束程序,并打印默认的出错信息)。

(3) 异常处理完毕,控制流就通过整个try语句(除非在处理异常时又引发新的异常)。finally: 无论系统有无异常代码均运行。
2. 异常说明
(1) except语句可以有多个,Python会按except语句的顺序依次匹配出现的异常,如果异常已经处理就不会再进入后面的except语句。
(2) except语句后面如果不指定异常类型,则默认捕获所有异常。可以通过logging或者sys模块获取当前异常。
(3) except语句可选,finally语句也可选,但是二者必须要有一个,否则try语句就没有意义了。
(4) except语句可以以元组形式同时指定多个异常。
【例5.7】异常捕获与处理代码示例。



while True:

try:

num=input('please input the number?')

for i in range(int(num)):

print(i)

break

except ValueError:

print("Oops!That was no valid number.Try again ")

finally:

print('Whatever, I always run!')



运行结果: 



please input the number?ss

Oops!That was no valid number.Try again 

Whatever, I always run!



please input the number?3

0

1

2

Whatever, I always run!



异常的参数: 



while True:

try:

num=input('please input the number?')





for i in range(int(num)):

print(i)

break

except ValueError as arg :

print(arg.args)

finally:

print('Whatever, I always run!')



一个异常可以带上参数,可作为输出的异常信息参数。



please input the number?rr

("invalid literal for int() with base 10: 'rr'",)

Whatever, I always run!

please input the number?2

0

1

Whatever, I always run!




变量接收的异常值通常包含在异常的语句中,保持在一个元组中(如上述例子错误("invalid literal for int() with base 10: 'rr'",)参数是一个元组),变量可以接收一个或者多个值。元组通常包含错误字符串、错误数字和错误位置。
5.4.2异常的抛出
Python程序除了可以捕获异常外,还可以在代码中使用raise语句主动抛出异常。raise语句的一般语法如下: 

raise [Exception [, args [, traceback]]]

其中: 
 Exception是异常的类型(例如,NameError),args是异常参数的值。
 参数是可选的; 如果没有提供,则异常参数为None。
 最后一个参数traceback也是可选的(在实践中很少使用),如果存在,则用于异常的追溯对象。
示例: 



x=input('input x:')

if (int(x)<5):

raiseNameError('HiThere')



运行结果: 



raiseNameError('HiThere')

NameError: HiThere



raise唯一的一个参数指定了要被抛出的异常。它必须是一个异常的实例或者是异常的类(也就是 Exception 的子类)。
【例5.8】raise主动抛出异常代码示例。



try:

x=input('input x:')

if (int(x)<5):

raise NameError('Hi There')

print(x)

except NameError as arg:

print(arg)



运行结果: 



input x:4

HiThere



5.5断言的用法
在开发一个程序时,与其让它运行时崩溃,不如在它出现错误条件时就崩溃(返回错误)。这时断言
(assert)就显得非常有用。
assert的语法格式: 

assert expression

它的等价语句为: 

if not expression:

raise AssertionError

【例5.9】assert代码示例。



>>> assert 1==1

>>> assert 1==0

Traceback (most recent call last):

File "<pyshell#1>", line 1, in <module>

assert 1==0

AssertionError



5.6with语句

with语句上下文管理器是一个对象,它定义了在运行with语句时要建立的运行时上下文(资源),它处理进入和退出所需运行时上下文,并运行代码块。它是对普通的try…except…finally使用模式进行封装以方便地重用。
with语句的语法为:

with expression [as target]

参数说明: 
 expression: 一个需要运行的表达式; 
 target: 一个变量或者元组,存储的是expression表达式运行返回的结果,它是可选参数。

相比try…except…finally语句,with语句更加简洁。
【例5.10】使用with语句打开文本文件c:/a.txt,并显示文件内容。



with open('c:/a.txt',encoding='utf-8') as f :

print(f.read())



5.7综合案例
Python中有些对象是可变的,有些对象是不可变的。可以用函数hash(object)来判定对象是否可变,如果hash(object)有返回值的是不可变。hash(object)抛出异常的是可变对象。以下针对list、tuple、range、str、bytes、bytearry、memoryview、set、frozenset、dict十类对象进行可变与不可变对象测试。
【例5.11】Python中可变对象与不可变对象代码测试(通过代码自动判读对象是否可以改变)。
测试对象为: 



a=[1,2,'hello']

b=set('hello')

c=dict(one=11, two=22, three=33)

d=bytearray('hello',encoding='utf-8')

e=(1,2,'hello')

f=range(1,6,2)

g='hello'

h=bytes('hello',encoding='utf-8')

i=memoryview(b'hello')

j=frozenset('hello')



测试代码为: 



test=[a,b,c,d,e,f,g,h,i,j]

for i in test:

try:

hash(i)

print(f'{type(i)} 是不可变对象')

except:

print(f'{type(i)} 是可变对象')





图5.4对象种类

图5.4所示对象种类与下述测试结果相符。

 <class 'list'>的实例是可变对象。
 <class 'set'>的实例是可变对象。
 <class 'dict'>的实例是可变对象。
 <class 'bytearray'>的实例是可变对象。
 <class 'tuple'>的实例是不可变对象。
 <class 'range'>的实例是不可变对象。
 <class 'str'>的实例是不可变对象。
 <class 'bytes'>的实例是不可变对象。
 <class 'memoryview'>的实例是不可变对象。
 <class 'frozenset'>的实例是不可变对象。
5.8本章小结
通过本章的学习,可以掌握if、while、for语句对程序流的控制,掌握程序发生异常的处理方法。
5.9习题












扫码观看



(1)  已知上海地铁1、2号线(为了简化问题,只考虑这两条线路)两条地铁站线路各站点名称(可以到http://service.shmetro.com/axlcz01/index.htm获取),通过Python编程获得“上海南站”到“金科路”的换乘路线。
(2) 模拟双色球投注与兑奖。使用循环随机投注10000注,给定一组中奖号码,判断有多少注号码中奖,计算中奖概率。
(3) 给定一个字典,请按照逆序输出字典的key和value。