写出来的python程序不一定是完全正确的。即使所有逻辑都对了,但是用户在使用过程的不当,也会导致程序运行出错。


本文结构:

  1. 常见的异常类型

  2. 如何处理异常

  3. 如何自定义异常



1. 常见的异常大概有以下的一些:

  • NameError         尝试访问一个没有声明的变量

   如图,在f() 函数里,试图打印一个未被定义的变量yy,当调用f()函数时,将会抛出一个NameError 的异常。

wKiom1gGJWzQ0yIzAAB1JU7Zw3E386.png


  • ImportError        无法引入模块或包;可能路径不存在

  如图,试图import 模块abc12,但是由于abc12 并不是已经被定义了的模块,所以程序就会抛出ImportError 的异常。

wKioL1gGJmXBTF2ZAAA-aeVuCU8757.png


  • IndentationError     语法错误(的子类);代码没有正确对齐

   如图1,如果空格键和Tab键混用,也是会抛出IndentationError 这个语法错误。a前用了空格键,b前用了tab键。

   如图2,就是明显的没有对齐了,语法错误。


wKioL1gGKlfA4tyJAAAc2CGcF1A722.png

wKiom1gGKleh-QBvAAAeEvsj2kQ697.png


  • SyntaxError        语法错误


  • IndexError         索引超出序列范围

   如图,访问列表时,访问了超出索引范围,就会抛出IndexError 异常

  wKioL1gGK8Kw2jDfAAAgbOLd45Q748.png


  • KeyError          请求一个不存在的字典关键字

   如图,对字典dic 的访问,使用了一个不存在的key值,就会抛出KeyError异常

   wKiom1gGLGmDoOYOAAAhvuT_xYE672.png


  • IOError           输入输出错误(比如要读的文件不存在)

    如图,用open() 函数尝试打开一个不存在的文件,则会抛出IOError异常。但是如果是使用‘w’的方式打开,则不存在的文件会被创建。

wKiom1gGLS-SJFWMAAA8MppxIfQ790.png


  • AttributeError      访问未知对象属性



  • ValueError         传给函数的参数类型不正确,比如int()函数传入字符串函数

   如图,给int()函数传入一个非数字的字符参数,会抛出ValueError 异常

  wKioL1gGMn3T8oTWAAAqxPRNOos596.png


  • UnboundLocalError    试图访问一个还未被设置的局部变量

   如图,变量y 在函数f()外面已经被定义,但是在f()方法里,它是一个局部变量,y还没使用,不能直接对其进行赋值操作。

   但是,在第二个f()函数的定义中,直接print 打印y值,这样做是不会抛出异常的。

   wKiom1gGNJeRSHzPAABD0ADCS5M368.png


  • KeyboardInterrupt    键盘中断异常,例如ctrl+c

   time.sleep(60)  让程序休眠60秒,在休眠的过程中,按下ctrl+c,将会终端time.sleep()进程,同时抛出KeyboardInterrupt异常。

   wKiom1gGNwWjmWcgAAAk2kVsZno088.png



2. 如何处理异常


  在python中处理异常,需要对异常捕获,再处理。在python中捕获异常的代码大概是这样的:

try:
    try_suite
except Exception1,Exception2,...,Argument:
    exception_suite
    #other exception block
else:
    no_exceptions_detected_suite
finally:
    always_execute_suite


如上,在try 里捕获异常,如果try_suite 抛出了异常,那么异常就会被捕获。


然后except 对捕获的异常类型进行判断,如果是想要捕获的异常类型,则在excepttion_suite 里做一些处理。

Exception1, Exception2 是Exception 异常类型对象,例如在文章第一部分列出的常见异常类型NameError,IndexError等异常类型。

Argument 


else 语句,是当try 没有捕获到任何异常时,就会执行no_exception_detected_suite 里的操作。但是,else 部分并不是必须的,它可以被省略。即使省略了else 部分,甚至将except 部分也省略了,也是可以的。只要有try 部分就可以对异常进行捕获了。


finally 语句,无论是否捕获到异常,异常处理是否被执行了,都一定会将finally部分执行的。所以,通常会将,文件关闭,资源回收这些操作放在finally部分执行。finally 部分也是可以省略的。


2.1 try...except... 结构

 大概的结构: 

try:
    try_suite
except Exception:
    except_suite

 只要try_suite 抛出异常,可能会被try 语句捕获,try 捕获后,就会将异常交给except 语句处理,如果异常是except 需要判定的Exception 则,执行except_suite 的语句。

例子:  

wKiom1gHabugXuujAAAdVjlzsMk705.png

如上图,try 捕获到ZeroDivisionError, 通过Except 判定是需要捕获的,则执行print 输出代码块。


try...except... 可以一次捕获多个错误,可以将异常写在同一个except 里,也可以分开在多个except 语句,例如:

wKiom1gHbcPz9lpWAABVERM6DqA512.png

如上图,分别用两个except 语句处理了try语句捕获到的ZeroDivisionError 和ValueError 的异常。


如果不知道需要捕获什么异常,except 可以什么异常类型都不带。例如:

wKiom1gHcC_AFXD2AAA3v6Qj_ek854.png

如图,两个函数里的try...except... 语句块里的except 都没有带具体的Exception类型,所以只要在try 语句里捕获到的任何异常,交给except 语句都会执行except_suite 语句。


当然,除了将多个异常写在不同的except 语句里,也可以将多个异常类型写在一个except 里,例如:

try :
    try_suite
except  Exception1, Exception2,...:
    except_suite


except 语句除了能捕获异常类型外,还可以创建异常类型的实例,通过异常的实例可以 知道有关异常的更多信息,这对我们跟踪和处理异常是很有帮助的。异常的实例,就是except Exception1,Exception2,...,Argument里的Argument参数了。Exception1,Exception2等都是异常的类型,如果try 捕获到的异常,是except 需要判定的,那么Argument 就会被except 创建为该类型的实例。通过实例,可以知道更多的异常信息。例如:

wKioL1gHdAKjz88mAABexMvKb_I044.png如图,try 捕获了一个除零异常,并且创建了一个异常类型的实例Argument。

通过打印Argume 的信息,可以更具体地知道异常的信息。



2.2 try...except...else...

try:
    try_suite
except Exceptions1,...argument:
    except_suite
else:
    other_suite

try...except... 就不用多说了。else类似于if...elsse... 结构的else.当try 没有捕获到异常,那么else 语句中的other_suite 语句将会执行。

wKioL1gHfFLBms-hAABX5LhugJ8141.png

如上图,第一次打开当前路径存在的文件,没有异常抛出,则执行了except 的语句。第二次,尝试打开一个当前路径不存在的文件,则抛出了IOError 的异常。


2.3 finally...子句

  finally 语句,无论有没有异常被捕获,它都会执行。可以try和finally 配合使用,也可以和except 和else一起配合使用。

例如:

try:
    fd = open('/home/hell/hell.txt')
except IOError, re:
    print "Error: no such  file"
else:
    print "cool~"
finally:
    print "end..."


3. 自定义异常

 通过自定义异常类,但是这个类需要继承父类Exception 异常类.

wKiom1gHiAbC27BoAAB-6dh_c0s650.png

 (1) 定义了一个继承自Exception 的自定义异常类FunError ,类里的__str__()方法被定义为return 一条信息,通常作为异常信息。

 (2) 定义一个f() 函数,在函数里抛出异常,使用关键字raise。raise 关键字可以用来抛出一个异常。

 (3) 所以,如果调用f() 函数,那么函数就会抛出一个FunError()的异常类。

 (4) 当抛出FunError() 异常类时,异常类的实例同时被创建,那么类的__str__() 方法就会被调用,所以,就可以看到异常信息FunError: I am a func Error.


所以,自定义的FunError 类,也是可以被try 来捕获的,例如:

wKiom1gHioiQrpTFAAAaAsHJ_Cg125.png

如图,在try 里调用f() 函数,它会抛出异常,被except 捕获,将异常类的实例e 打印,打印的就是类的__str__() 方法的信息。