第11课 异常处理

一、异常处理的意义

1. 异常机制已经成为衡量一门编程语言是否成熟的标准之一,使用异常处理机制的Python程序会有更好的容错性。

2. 没有人能够保证自己写的程序永远不会出错,既是程序没有错误,也不能保证用户按你的意图来输入,另外还有系统的稳定性,计算硬件是否损坏,网络掉线等诸多情况。

二、异常处理机制

1. 使用try...except捕获异常

1)例如,通常在程序运行时,用户可以随意输入,程序不会因为用户的输入不合法而突然间退出,而是向用户提示输入不合法,并请用户再次输入。

那么我们就希望有一种强大的if块来解决这个非法输入的问题:

if 用户输入不合法:

    alert 输入不合法

    go retry

else:

    #业务实现代码

2)但是“用户输入不合法”这个条件怎么定义呢?我们可以使用正则表达式与用户的输入进行匹配。但现实中不合法的情况非常多,想让程序一次处理所有的错误,我们可以将上面的伪代码修改为:

if 一切正常:

    #业务实现代码

就是:

    alert 输入不合法

    goto retry

3) 但“一切正常”依然是很抽象的,无法转化成代码。在这种情形下Python提供了一种假设:如果程序可以顺利运行,那么就是“一切正常”。由此得出Python异常处现机制的语法结构:

try:

    #业务实现代码

except(Error1, Error2, ....) as e:

    alert 输入不合法

    goto retry

4) 这样如果执行try块里的业务实现代码是出现异常,系统会自动生成一个异常对象,该异常对象被提交给Python解释器,这个过程被称为引发异常。

5)当Python解释器收到异常对象时,会寻找处理该异常对象的except块,如果找到合适的except块,则把该过程称为捕捉异常。如果Python解释器找到不捕获异常的except块,则运行时环境终止,Python解释器也将退出。

6)不管代码块是否处于try中,或except块中,只要执行该代码时了现了异常,系统总会自动生成一个Error对象。

7) try 可以有多个except块,这是为了针对不同的异常类提供不同的异常处理方式。当系统发生不同意外情况时,系统会生成不同的异常对象。如果try被执行一次,,则try后面只有一个except被执行,除非放在循环中,使用continue开始下一次循环。

2. 异常类

1)Python的所有异常类的基类是BaseException, 但是如果用户果自定义异常,则一概继承Exception类。

2)BaseException的主要子类是Exception,所在不管是系统的异常类,还是用户自定义异常类,都是从Exception派生。

3)异常捕获实例:

>>> import sys

>>> try:

a = int(sys.argv[1]) # 代表当前运行的程序所提供的第一个参数

b = int(sys.argv[2])# 代表当前运行的程序所提供的第二个参数

c = a/b

print("您输入的两个数相除的结果是:",c)

except IndexError:

print("索引错误:运行程序时输入的参数个数不够")

except ValueError:

print("数值错误:程序只能接受整数参数")

except ArithmeticError:

print("算术错误")

except Exception:

print("未知异常")

3. 多异常捕获:指一个Except块可以捕获多种类型的异常。例如:

>>> import sys

>>> try:

a = int(sys.argv[1])

b = int(sys.argv[2])

c = a/b

print("您输入的两个数相除的结果是:",c)

except (IndexError, ValueError, ArithmeticError):

print("程序发生了数组越界、格式错误、算术异常之一")

except: # 此处的省略也是合法的,一般放在最后

print("未知异常")

程序发生了数组越界、格式错误、算术异常之一

4. 访问异常信息

1)如果程序需要在except块中访问异常对象的相关信息,则可以通过为异常对象声明变量来实现。

2)所在的异常对象都包含如下几个对象和方法:

args: 该属性返回异常的错误编号和描述字符串

errno:该属性返回异常的错误编号

strerror:该属性返回异常的描述字符串

with_traceback():通过该方法可以处理异常的传播轨迹

3)程序访问异常信息实例:

>>> def foo():

try:

fis = open("a.txt")

except Exception as e:

print(e.args)

print(e.errno)

print(e.strerror)

>>> foo()

#运行结果如下:

(2, 'No such file or directory')

2

No such file or directory

5. else块

1) 在Python异常处理流程中还可添加一个else块,当try块没有出现异常时,程序会执行else块。

例如:

>>> s = input("请输入除数:")

请输入除数:5

>>> try:

result = 20/int(s)

print('20除以%s的结果是:%g'%(s,result))

except ValueError:

print('值错误,您必须输入数值')

except ArithmeticError:

print('算术错误,您不能输入0')

else:

print('没有出现异常')

#运行结果如下:

20除以5的结果是:4

没有出现异常

2)实际上大部分语言异常处理都没有else块,可以将else块的内容直接放到try后面。但Python异常处理使用else块也不是多余的语法。因为在else块中的异常不会被except捕获,该异常会传给Python解释器导致程序中止。

3)如果希望某段代码的异常,能被后面的except捕获,那么就应该放在try块中;如果不希望被except捕获,就应该放在else块中。

6. 使用finally回收资源

1)为了能够保证回收try块中打开的一些物理资源(如数据库连接、网络连接和磁盘文件),异常处理机制提供了finally块。

2)Python 完整的异常处理语法结构如下:

try:

    #业务实现代码

except SubException1 as e:

    #异常处理块1

except SubException2 as e:

    #异常处理块2

else:

    #正常处理块

finally:

    #资源回收块


三、使用raise引发异常

1. 如果程序中数据或执行与现实的需求不符,但是系统不会判断这样的异常,只能程序员来决定是否引发异常,此时可以使用raise语句来完成这种自行引发的异常。

2. raise语句三种常见的用法:

1) 单独一个raise,该语句引发当前上下文中捕获的异常,或默认引发RuntimeError异常。

2)raise后带一个异常类,该语句引发指定的异常类。

3)raise后带一个异常对象,该语句引发制定的异常对象。

以上三种最终都是引发一个异常实例,每次只能引发一个异常实例。

3. 用户引发异常的两种方式:raise和except,例如:

>>> def main():

try: # 使用try...except来捕获异常,此时出现异常也不会传给调用它的main()函数

mtd(3)

except Exception as e:

print('程序出现异常类是:',e)

mtd(3) #不使用try...except来捕获异常,异常会传播并导致程序中止

>>> def mtd(a):

if a>0:

raise ValueError("a的值大于0,不符合要求")

>>> main()


四、异常的传播轨迹

1. 异常只要没有被完全捕获,异常就会从发生异常的函数或方法向外传播,首先传给该函数或方法的调用者,然后,,直到传给Python解释器,Python解释器就会中止程序,并打印异常传播的轨迹信息。所以通常我们看到大段的异常信息,并不一定发生很多严重的问题,可能只是一个异常引发的。

2. Python专门提供trackback模块来处理异常传播的轨迹,两种常用的方法是:

1)trackback.print_exc(): 将异常传播轨迹输出到控制台或文件中。

2) format_exc():将异常传播轨迹转换成字符串。

五、异常处理规则

1.  不要过度使用异常处理,注意以下两点:

1) 需要编写错误处理代码,而不是简单的用异常来代替处理。

2)  不能用异常来代替流程控制。

2.  不要使用过度庞大的try块:try块越大,发生异常的可能性就越大。而且在庞大的try块后势必有大量的except块,这时要判断各个块之间的逻辑关系,编写程序会变得更加复杂。

3. 不要忽略捕捉到的异常: 程序应该尽量修改异常,保证程序继续运行;不要将本层的异常传给上一层去处理。

六、本节回顾

1. 你怎么理解异常处理机制?

2. Python异常处理的5个关键字是什么?(try\ except\else\finally\raise)

3. 如何使用raise引发异常?

4. 如何获得异常的源头和轨迹?

5.异常处理的原则有哪些?

你可能感兴趣的:(第11课 异常处理)