Python学习笔记十四:异常

1、什么是异常

在学习Python编程的过程中,经常会看到一些报错信息,使你编写的程序不能够如期工作,如我们前面看到过得NameError、SyntaxError、TypeError、VauleError等,这些都是异常。

异常是一个事件,该事件会在程序执行过程中发生,影响程序的正常执行。一般情况下,在Python无法正常处理程序时就会发生异常。异常是Python的对象,表示一个错误。当Python脚本发生异常时,我们需要捕捉并处理异常,否则程序会终止执行。

每一个异常都是一些类的实例,这些实例可以被引用,并且可以用很多种方法进行捕捉,使得错误可以被处理,而不是让整个程序失败。

2、异常处理

出现异常怎么办呢?

就如我们使用的工具出现了一点小毛病,我们可以想办法修理好它。程序也一样,前辈们经过不断累积与思考,创造了不少好方法处理程序中的异常,最简单的是使用try语句处理。

try语句的基本形式为 try/except。try/except语句用于检测try语句块中的错误,从而让except语句捕捉异常信息并处理。如果不想在发生异常时结束程序,只需在try语句块中捕获异常即可。

捕获异常的语句如下:

try:
    <语句>                    #运行别的代码
except <语句>:
    <语句>                    #如果在try部分引发了异常

try的工作原理是,开始一个try语句后,Python就在当前程序的上下文中作标记,当出现异常时就可以回到做标记的地方。首先执行try子句,接下来发生什么依赖于执行时是否出现异常。

如果try后的语句执行时发生异常,程序就跳回try并执行except子句。异常处理完毕,控制流就可以通过整个try语句了(除非在处理异常时又引发新异常)。

#! /uer/bin/python3
#-*-coding:UTF-8-*-

def exp_exception(x,y):
   try
      a = x/y
      print(‘a=’ , a)
      return  a
   except Exception:
      print(‘程序出现异常,异常信息:被除数为0’)

exp_exption(2,0)

程序出现异常,异常信息:被除数为0

由执行结果看到,程序最后执行的是except子句,如果语句正常,应该输出“a=”的形式。
这里可以会有疑问:直接在做除法前对y值进行判断不就解决问题了,何必使用try/except语句呢?

在本例中这么做确实更好一些,如果给程序加入更多除法,就得给每个除法语句加一个判断语句,这样整个代码看上去就是一堆类似if的功能重复判断语句,真正有效的代码没少,而使用try/except只需要一个错误处理器即可。

注意:
如果没有处理异常,异常就会被“传播”到调用的函数中,如果在调用的函数中依然没有处理,异常就会继续“传播”,直到程序的最顶层。也就是可以处理其他人程序中未处理的异常。

3、抛出异常

Python使用raise语句抛出一个指定异常,我们可以使用类或实例参数调用raise语句引发异常,使用类似程序会自动创建实例。

>>>raise Exception
Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    raise Exception
Exception
>>> raise NameError ('This is NameError')
Traceback (most recent call last):
  File "<pyshell#4>", line 1, in <module>
    raise NameError ('This is NameError')
NameError: This is NameError

由操作结果看到,第一个实例raise Exception引发了一个没有相关错误信息的普通异常,第二个实例输出了一些错误提示。

如果只想知道是否抛出了异常,并不想处理,使用一个简单的raise语句就可以再次把异常抛出。

>>> try:
    raise NameError ('This is NameError')
except NameError:
    print('An exception happened!')

An exception happened!

>>> try:
    raise NameError ('This is NameError')
except NameError:
    print('An exception happened!')
    raise

An exception happened!
Traceback (most recent call last):
  File "<pyshell#17>", line 2, in <module>
    raise NameError ('This is NameError')
NameError: This is NameError

由输出结果看到,使用raise可以输出更深层的异常,在使用过程中,可以借助该方法得到更详尽的异常信息。

我们前面碰到的NameError、SyntaxError、TypeError、VauleError等异常类称为内建异常类。在Python中,内建的异常类有很多,可以使用dir函数列出异常类的内容,并用在raise语句中,用法如raise NameError这般。

Python 重要的内建异常类

异常名称 描述
Exception 常规错误的基类
AttributeError 对象没有这个属性
IOError 输入/输出操作失败
IndexError 序列中没有此索引(index)
KeyError 映射中没有这个键
NameError 未声明/初始化对象(没有属性)
SyntaxError Python语法错误
SystemError 一般解释器系统错误
ValueError 传入无效的参数

4、捕捉多个异常

若出现多个异常的话,Python支持在一个try/except语句中处理多个异常:

try:
    <语句>         #运行别的代码
except <name1>:
    <语句>         #如果在try部分引发了name1异常
except2 <name2>:
    <语句>         #如果在try部分引发了name2异常

执行流程:
首先,执行try子句(在关键字try和关键字except之间的语句)。如果没有发生异常,忽略except子句,try子句执行后结束。如果在执行try子句的过程中发生异常,try子句余下的部分就会被忽略。如果异常的类型和except之后的名称相符,对应的except子句就会被执行。最后执行try语句之后的代码。如果一个异常没有与任何except匹配,这个异常就会传递到上层的try中。一个try语句可能包含多个except子句,分别处理不同的异常,但最多只有一个分支会被执行。

处理程序将只针对对应try子句中的异常进行处理,而不会处理其他异常语句中的异常。

def mult_exception(x,y):
    try :
         a=x/y
         b=name
    except ZeroDivisionError:
         print(‘this is ZeroDivisionError’)
    except NameError:
         print(‘this is NameError’)

mult_exception(2,0)

this is ZeroDivisionError

由执行结果看到,一个try可包含多个except子句,但子句中只有一个分支会被处理。

5、使用块捕捉异常

def  modle_exception(x,y):
     try:
          b=name
          a=x/y
     except(ZeroDivisionError,NameError,TypeError):
          print(‘onr of ZeroDivisionError or NameError or TypeError happened’)

modle_exception(2,0)

onr of ZeroDivisionError or NameError or TypeError happened

由执行结果看到,如果需要使用一个块捕捉多个类型异常,可以将它们作为元组列出。使用该方式时,遇到的异常类型时元祖中的任意一个,都会走异常流程。

这样子做有什么好处呢?假如我们希望多个except子句输出同样的信息,就没有必要在几个except子句中重复输入子句了,放到一个异常块中即可。

6、捕捉对象

1)捕捉对象

如果希望在except子句中访问异常对象本身,也就是看到一个异常对象真正的异常信息,而不是输出自己定义的异常信息,可以使用as e的形式,我们称之为捕捉对象。

def modle_exception(x,y):
    try:
        b=name
        a=x/y
     except (ZeroDivisionError,NameError,TypeError) as e:
        print(e)

model_exception(2,0)

name ‘name’ is not defined

有输出结果可知,执行过程中抛出的异常被截获并正常输出了相关异常信息,并且使用这种方式可以捕捉多个异常。此处的e也可以使用其他字母,用e意义比较明确,取自except。

2)全捕捉

在实际编码过程中,及时程序能处理好几种类型的异常,但有一些异常还是会从我们手中溜走。对于这种情况我们根本无法预测会发什什么,也无法提前做任何准备。

def modle_exception(x,y):
    try:
        b=name
        a=x/y
     except :
        print(‘Error happend’)

model_exception(2, ‘’)

Error happend

由程序和执行结果看到,可以在except子句中忽略所有异常类,从而让程序员输出自己定义的异常信息。

7、异常中的else

如果程序执行完异常还需要做其他事情,异常为我们提供了try…except…else语句实现该功能:

try:
    <语句>    #运行别的代码
except <name1>:
    <语句>    #如果在try部分引发了异常name1
except <name2>,<data>:
    <语句>    #如果引发了异常name2,获得附加数据data
else:
    <语句>    #如果没有发生异常

如果在try子句执行时没有发生异常,就会执行else语句后的语句(如果有else)。如果else子句比把所有语句都放在try子句里面更好,这样可以避免一些我们想不到而except又没有捕捉的异常。

8、自定义异常

尽管内建异常类包含了大部分异常,而且可满足很多要求,但有时还是要创建自己的异常类。比如需要精确制导问题的根源,就需要使用自定义异常精确定位问题。可以通过创建一个新exception类拥有自己的异常。异常应该继承自Exception类,可以直接继承,也可以间接继承。

因为错误就是累,捕捉一个错误就是捕获该类的一个实例,因此错误并不是凭空产生的,而是由一些不合理的部分导致的。Python的内置函数会抛出很多类型的错误,我们自己编写的函数也可以抛出错误。如果要抛出错误,那么可以根据需要定义一个错误的类,选择好继承关系,然后用raise语句抛出一个错误的实例。

class MyError(Exception):
       def _init_(self):
             pass
       def _str_(self):
             return ‘this is self define error’

def my_error_test():
     try:
         raise MyError()
      except MyError as e:
         print(‘exception info:’ , e)

my_error_test()

exception info: this is self define error

由程序和执行结果看到,程序正确执行了自定义的异常,并且需要继承Exception类。
异常最好以Error结尾,一方面贴近标准异常的命名,另一方面便于见名知意。

9、finally子句

Python中的finally子句需要和try子句一起使用,组成try/finally的语句形式,try/finally语句无论发生异常与否都将执行最后的代码:

def use_finally(x,y):
     try:
          a=x/y
     finally:
          print(‘No matter what happened,I will show in front of you’)

use_finally(2,0)

由执行结果看到,finally子句被执行了,无论try子句中是否发生异常,finally都会被执行。

猜你喜欢

转载自blog.csdn.net/viatorsun/article/details/80255978