Python异常处理

1.异常定义

异常即非正常状态,在Python中使用异常对象来表示异常。若程序在编译或运行过程中发生错误,程序的执行过程就会发生改变,抛出异常对象,程序流进入异常处理。如果异常对象没有被处理或捕捉,程序就会执行回溯(Traceback)来终止程序。

  • 异常的分类:
  1. 程序遇到逻辑或算法错误

  2. 运行过程中计算机错误:内存不够或者io错误

2.异常处理的定义

python解释器检测到错误,触发异常(也允许程序员自己触发异常)

程序员编写特定的代码,专门用来捕捉这个异常(这段代码与程序逻辑无关,与异常处理有关)

如果捕捉成功则进入另外一个处理分支,执行你为其定制的逻辑,使程序不会崩溃,这就是异常处理

3.异常处理的意义

python解析器去执行程序,检测到了一个错误时,触发异常,异常触发后且没被处理的情况下,程序就在当前异常处终止,后面的代码不会运行,所以你必须提供一种异常处理机制来增强你程序的健壮性与容错性 ,友好性,在程序出现bug时一般不会将错误信息显示给用户,而是出现一个提示的页面。

4.异常处理语法 

(1)try..except..else

注意:except子句的数量没有限制,但使用多个except子句捕获异常时,如果异常类之间具有继承关系,则子类应该写在前面,否则父类将会直接截获子类异常。放在后面的子类异常也就不会执行。 
格式

[python]  view plain  copy
  1. try:  
  2.     可能触发异常的语句块  
  3. except [exceptionType]:  
  4.     捕获可能触发的异常[可以指定处理的异常类型]  
  5. except [exceptionType][,date]:  
  6.     捕获异常并获取附加数据  
  7. except:  
  8.     没有指定异常类型,捕获任意异常  
  9. else:  
  10.     没有触发异常时,执行的语句块  
try的工作原理: 

执行一个try语句时,python解析器会在当前程序流的上下文中作标记,当出现异常后,程序流能够根据上下文的标记回到标记位,从而避免终止程序。 
1. 如果try语句执行时发生异常,程序流跳回标记位,并向下匹配执行第一个与该异常匹配的except子句,异常处理完后,程序流就通过整个try语句(除非在处理异常时又引发新的异常)。 
2. 如果没有找到与异常匹配的except子句(也可以不指定异常类型或指定同样异常类型Exception,来捕获所有异常),异常被递交到上层的try(若有try嵌套时),甚至会逐层向上提交异常给程序(逐层上升直到能找到匹配的except子句。实在没有找到时,将结束程序,并打印缺省的错误信息)。 
3. 如果在try子句执行时没有发生异常,python将执行else语句后的语句(可选),然后控制流通过整个try语句

需求:将用户输入的两个数字相加

复制代码
while True:
    num1 = raw_input('num1:')
    num2 = raw_input('num2:')
    try:
        num1 = int(num1)
        num2 = int(num2)
        result = num1 + num2
    except Exception, e:
        print '出现异常,信息如下:'
        print e
复制代码

(2)try..except..else..finally

try:
    msg = input(">>")
    print(int(msg))
except Exception as e:
    print("异常的类型是:%s"%type(e))
    print("异常的内容是:%s"%e)
else:
    print('如果代码块不抛出异常会执行此行代码!')
finally:
    print('不管代码块是否抛出异常都会执行此行代码!')
  • 运行结果:
>>34
34
如果代码块不抛出异常会执行此行代码!
不管代码块是否抛出异常都会执行此行代码!

(3)万能异常 

在python的异常中,有一个万能异常:Exception,他可以捕获任意异常,即:

1
2
3
4
5
s1  =  'hello'
try :
     int (s1)
except  Exception,e:
     print  e

既然有这个万能异常,其他异常是不是就可以忽略了???

答:当然不是,对于特殊处理或提醒的异常需要先定义,最后定义Exception来确保程序正常运行。不推荐使用

1
2
3
4
5
6
7
8
9
s1  =  'hello'
try :
     int (s1)
except  KeyError,e:
     print  '键错误'
except  IndexError,e:
     print  '索引错误'
except  Exception, e:
     print  '错误'
捕捉多个异常

方法一:指定一个通用异常,可以捕获多个不同的包含在Exception类中的异常类

  1. try:  
  2.     语句块  
  3. except Exception:  
  4.     语句块  
方法二:在一个except子句后将多个异常作为元组元素列出
  1. try:  
  2.     语句块  
  3. except (IOError,ValueError):   
  4.     语句块  
方法三:except子句后不带任何异常名称,捕获所有异常
  1. try:  
  2.     语句块  
  3. except:   
  4.     语句块  
  1. except语句不是必须的,finally语句也不是必须的,但是二者必须要有一个,否则就没有try的意义了。
  2. except语句可以有多个,Python会按except语句的顺序依次匹配你指定的异常,如果异常已经处理就不会再进入后面的except语句。
  3. except语句可以以元组形式同时指定多个异常,参见实例代码。
  4. except语句后面如果不指定异常类型,则默认捕获所有异常,你可以通过logging或者sys模块获取当前异常。
  5. 如果要捕获异常后要重复抛出,请使用raise,后面不要带任何参数或信息。
  6. 不建议捕获并抛出同一个异常,请考虑重构你的代码。
  7. 不建议在不清楚逻辑的情况下捕获所有异常,有可能你隐藏了很严重的问题。
  8. 尽量使用内置的异常处理语句来替换try/except语句,比如with语句,getattr()方法。

5.Python中的异常处理机制 

在Python当中,若一个程序在运行的时候出错,Python解释器会自动的在出错的地方生成一个异常对象,而后Python解释器会自动的在出错地方的附近寻找有没有对这个异常对象处理的代码,所谓异常处理代码就是try……except语句,如果没有,Python解释器会自动的将这个异常对象抛给其调用函数,就这样层层抛出,如果在main当中也没有对这个异常对象处理的代码,Python解释器(实际上是操作系统)最后会做一个简单粗暴的处理,将整个程序给终止掉,并将错误的信息在显示屏上输出。

6.Python中常见异常种类

python中的异常种类非常多,每个异常专门用于处理某一项异常!!!

AttributeError 试图访问一个对象没有的树形,比如foo.x,但是foo没有属性x
IOError 输入/输出异常;基本上是无法打开文件在,做文件操作的时候常遇到的错误,这里介绍一下FileNotFoundError 就是做文件操作的时候文件不存在
ImportError 无法引入模块或包;基本上是路径问题或名称错误
IndentationError 语法错误(的子类) ;代码没有正确对齐,缩进问题
IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
KeyError 试图访问字典里不存在的键
KeyboardInterrupt Ctrl+C被按下
NameError 使用一个还未被赋予对象的变量
SyntaxError Python 语法错误,少了个冒号啊,没有空格啊,都会出现这个错误。
TypeError 传入对象类型与要求的不符合
UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
导致你以为正在访问它
ValueError 传入一个调用者不期望的值,即使值的类型是正确的

python所有的标准异常类:

异常名称描述
BaseException所有异常的基类
SystemExit解释器请求退出
KeyboardInterrupt用户中断执行(通常是输入^C)
Exception常规错误的基类
StopIteration迭代器没有更多的值
GeneratorExit生成器(generator)发生异常来通知退出
SystemExitPython 解释器请求退出
StandardError所有的内建标准异常的基类
ArithmeticError所有数值计算错误的基类
FloatingPointError浮点计算错误
OverflowError数值运算超出最大限制
ZeroDivisionError除(或取模)零 (所有数据类型)在做数据处理和计算的时候会遇到这种错误
AssertionError断言语句失败
AttributeError对象没有这个属性
EOFError没有内建输入,到达EOF 标记
EnvironmentError操作系统错误的基类
IOError输入/输出操作失败
OSError操作系统错误
WindowsError系统调用失败
ImportError导入模块/对象失败
KeyboardInterrupt用户中断执行(通常是输入^C)
LookupError无效数据查询的基类
IndexError序列中没有没有此索引(index)
KeyError映射中没有这个键
MemoryError内存溢出错误(对于Python 解释器不是致命的)
NameError未声明/初始化对象 (没有属性)
UnboundLocalError访问未初始化的本地变量
ReferenceError弱引用(Weak reference)试图访问已经垃圾回收了的对象
RuntimeError一般的运行时错误
NotImplementedError尚未实现的方法
SyntaxErrorPython 语法错误
IndentationError缩进错误
TabErrorTab 和空格混用
SystemError一般的解释器系统错误
TypeError对类型无效的操作
ValueError传入无效的参数
UnicodeErrorUnicode 相关的错误
UnicodeDecodeErrorUnicode 解码时的错误
UnicodeEncodeErrorUnicode 编码时错误
UnicodeTranslateErrorUnicode 转换时错误
Warning警告的基类
DeprecationWarning关于被弃用的特征的警告
FutureWarning关于构造将来语义会有改变的警告
OverflowWarning旧的关于自动提升为长整型(long)的警告
PendingDeprecationWarning关于特性将会被废弃的警告
RuntimeWarning可疑的运行时行为(runtime behavior)的警告
SyntaxWarning可疑的语法的警告
UserWarning用户代码生成的警告

7.主动触发异常

如果你需要自主抛出异常一个异常,可以使用raise关键字,等同于C#和Java中的throw,其语法规则如下。

raise NameError("bad name!")格式raise [exceptionType[,argument][,traceback]]
  1. def testRaise(number):  
  2.     if number < 1:  
  3.         raise ValueError('Invalid value'#或者 raise ValueError,'Invalid value'  
  4. testRaise(0)  

raise关键字后面可以指定你要抛出的异常实例,一般来说抛出的异常越详细越好,Python在exceptions模块内建了很多的异常类型,通过使用dir()函数来查看exceptions中的异常类型,如下:

import exceptions

print dir(exceptions)
# ['ArithmeticError', 'AssertionError'...]

当然你也可以查阅Python的文档库进行更详细的了解。

执行raise语句时,python会创建指定异常类的对象,还能够指定对异常对象进行初始化的参数,参数也可以为由若干参数组成的元组。 
注意:一旦执行raise语句,程序就会被终止。 

1
2
3
4

如果输入的数据不是整数,则引发一个ValueError

1
2
3
4
5
inputValue = input ( "please input a int data :" )
if  type (inputValue)! = type ( 1 ):
     raise  ValueError
else :
     print  inputValue

假设输入1.2,运行结果为:

please input a int data :1.2
Traceback (most recent call last):
File "C:/Users/lirong/PycharmProjects/untitled/openfile.py", line 3, in <module>
raise ValueError
ValueError

如果输入1,运行结果为:

please input a int data :1
1

8.打印异常信息

复制代码
def Mysql_select(db_host,sql):
    try:
        cnx = pymysql.connect(host='192.168.1.15',port= 3306,user = 'root',passwd='123123',db='test',timeout=5)
    # except TypeError:
    #     print 'In typeerror'
    except Exception as e:
        print repr(e)

as获取异常信息

每个异常都会有一定的描述信息,可以通过as关键字来获取。但是这种异常信息并不适合一般用户阅读,所以会使用自定义的异常信息。但是仍然会将原有的异常信息保留起来,用于后期的异常分析

[python]  view plain  copy
  1. #!/usr/bin/env python  
  2. try:  
  3.     try:  
  4.         openFile = open('notExistsFile.txt','r')  
  5.         fileContent = openFile.readlines()  
  6.     except (IOError,ValueError) as info:  #或者except (IOError,ValueError),info:   
  7.         print info  
  8. except:  
  9.     print 'process exception'  
  10. else:  
  11.     print 'Reading the file'  
output
[python]  view plain  copy
  1. In [164]: %run testError.py  
  2. [Errno 2] No such file or directory: 'notExistsFile.txt'  
  3. Reading the file  

异常参数

也可以使用异常参数作为输出的异常信息参数,来获取异常信息。并且异常参数中包含有异常信息、错误数字、错误位置等属性

[python]  view plain  copy
  1. #!/usr/bin/env python  
  2. try:  
  3.     try:  
  4.         openFile = open('notExistsFile.txt','r')  
  5.         fileContent = openFile.readlines()  
  6.     except (IOError,ValueError),info:  
  7.         print dir(info)  
  8.         print info.args  
  9. except:  
  10.     print 'process exception'  
  11. else:  
  12.     print 'Reading the file'  
output
[python]  view plain  copy
  1. In [44]: %run test.py  
  2. ['__class__''__delattr__''__dict__''__doc__''__format__''__getattribute__''__getitem__''__getslice__''__hash__''__init__''__new__''__reduce__''__reduce_ex__''__repr__''__setattr__''__setstate__''__sizeof__''__str__''__subclasshook__''__unicode__''args''errno''filename''message''strerror']  
  3. (2'No such file or directory')  
  4. Reading the file  

traceback追踪异常

使用traceback追踪异常的时候,需要import traceback模块。traceback模块可以有效的帮助查看异常的详细信息。 
注意:若希望获取异常的详细信息,却又不会终止程序的执行,可以在except子句中使用tarceback.print_exc()函数。 
tarceback.print_exc(): 
print_exc(limit=None, file=None) 
Shorthand for ‘print_exception(sys.exc_type, sys.exc_value, sys.exc_traceback, limit, file)’. 
(In fact, it uses sys.exc_info() to retrieve the same information in a thread-safe way.) 
输出sys.exc_type, sys.exc_value, sys.exc_traceback, limit, file等异常信息,实际上是以线程安全的方式去使用sys.exc_info()函数来获取相同的信息

[python]  view plain  copy
  1. #!/usr/bin/env python  
  2. import traceback  
  3.   
  4. try:  
  5.     openFile = open('notExistsFile.txt','r')  
  6.     fileContent = openFile.readlines()  
  7. except IOError as info:  
  8.     print 'File not Exists'  
  9.     print info  
  10.     traceback.print_exc()  
  11.     print 'continue'  
  12. except:  
  13.     print 'process exception'  
  14. else:  
  15.     print 'Reading the file'  
output
[python]  view plain  copy
  1. In [38]: %run test.py  
  2. File not Exists  
  3. [Errno 2] No such file or directory: 'notExistsFile.txt'  
  4. Traceback (most recent call last):  
  5.   File "/usr/local/src/pyScript/test.py", line 5in <module>  
  6.     openFile = open('notExistsFile.txt','r')  
  7. IOError: [Errno 2] No such file or directory: 'notExistsFile.txt'  
  8. continue  
异常信息的重定向:如果希望将异常的信息保存在一个指定的文件中,以供后期分析。可以使用下面的方法
[python]  view plain  copy
  1. #!/usr/bin/env python  
  2. import traceback  
  3.   
  4. try:  
  5.     with open('notExistsFile.txt','r') as openFile:  
  6.         fileContent = openFile.readlines()  
  7. except IOError:  
  8.     with open('errorLog','w+') as errorInfo:  
  9.         traceback.print_exc(file=errorInfo)  
  10.     print 'continue'  
  11. except:  
  12.     print 'process exception'  
  13. else:  
  14.     print 'Reading the file'  
output
[python]  view plain  copy
  1. In [61]: %run test.py  
  2. continue  
  3.   
  4. In [62]: cat errorLog  
  5. Traceback (most recent call last):  
  6.   File "/usr/local/src/pyScript/test.py", line 5in <module>  
  7.     with open('notExistsFile.txt','r') as openFile:  
  8. IOError: [Errno 2] No such file or directory: 'notExistsFile.txt'  
sys.exc_info()获取异常信息

traceback.print_exc()函数实际上是call sys.exc_info()

[python]  view plain  copy
  1. import sys    
  2. try:    
  3.     a=b    
  4.     b=c    
  5. except:    
  6.     info=sys.exc_info()    
  7.     print info[0],":",info[1]    
output
[python]  view plain  copy
  1. In [65]: %run test.py  
  2. <type 'exceptions.NameError'> : name 'b' is not defined  

1、str(e)

  返回字符串类型,只给出异常信息,不包括异常信息的类型,如1/0的异常信息

  ‘__init__() got an unexpected keyword argument 'timeout'’

2、repr(e)

  给出较全的异常信息,包括异常信息的类型,如1/0的异常信息

  TypeError("__init__() got an unexpected keyword argument 'timeout'",)

3、e.message

  获得的信息同str(e)

4、采用traceback模块

  需要导入traceback模块,此时获取的信息最全,与python命令行运行程序出现错误信息一致。使用traceback.print_exc()打印异常信息到标准错误,就像没有获取一样,或者使用traceback.format_exc()将同样的输出获取为字符串。你可以向这些函数传递各种各样的参数来限制输出,或者重新打印到像文件类型的对象。

9.自定义异常类型

Python中自定义自己的异常类型非常简单,只需要要从Exception类继承即可(直接或间接):

class SomeCustomException(Exception):
    pass
class AnotherException(SomeCustomException):
    pass

一般你在自定义异常类型时,需要考虑的问题应该是这个异常所应用的场景。如果内置异常已经包括了你需要的异常,建议考虑使用内置的异常类型。比如你希望在函数参数错误时抛出一个异常,你可能并不需要定义一个InvalidArgumentError,使用内置的ValueError即可。

自定义的异常类只能通过raise关键字来手动触发

[python]  view plain  copy
  1. class testError(Exception):    #直接集成Exception类  
  2.     def __init__(self,arg):  
  3.         self.args = arg  
  4. try:  
  5.     raise testError('Just test')  
  6. except testError,info:  
  7.     print info.args  
output
[python]  view plain  copy
  1. In [52]: %run test.py  
  2. ('J''u''s''t'' ''t''e''s''t')  
class  WupeiqiException(Exception):  
     def  __init__( self , msg):
         self .message  =  msg
     def  __str__( self ):
         return  self .message
try :
     raise  WupeiqiException( '我的异常' )
except  WupeiqiException,e:
     print  e

with..as触发异常自动关闭资源

在使用类文件的流对象时,都需要单独的调用close()来关闭资源。with..as语句能够实现在with语句块执行完后,自动的关闭文件。如果with语句块中触发异常,会调用默认的异常处理器处理,而且文件仍然能够正常关闭

[python]  view plain  copy
  1. #!/usr/bin/env python  
  2. import os  
  3. def testWith(fileName):  
  4.     try:  
  5.         with open(fileName,'r+') as pwd:  
  6.             pwd.readlines()  
  7.             print 2/0  
  8.     except Exception:  
  9.             print 'File closed:',pwd.closed  #判断文件是否关闭  
  10. if __name__ == '__main__':  
  11.     if os.path.exists('/usr/local/src/pyScript/fileOperation.txt'):  
  12.         testWith('/usr/local/src/pyScript/fileOperation.txt')  
  13.         print 'continue'  
output
[python]  view plain  copy
  1. In [17]: %run test.py  
  2. File closed: True    #没有call close()函数,文件仍然自动关闭。  
  3. continue  

10.传递异常 re-raise Exception

捕捉到了异常,但是又想重新抛出它(传递异常),使用不带参数的raise语句即可:

def f1():
    print(1/0)

def f2():
    try:
        f1()
    except Exception as e:
        raise  # don't raise e !!!

f2()

在Python2中,为了保持异常的完整信息,那么你捕获后再次抛出时千万不能在raise后面加上异常对象,否则你的trace信息就会从此处截断。以上是最简单的重新抛出异常的做法,也是推荐的做法。

还有一些技巧可以考虑,比如抛出异常前你希望对异常的信息进行更新。

def f2():
    try:
        f1()
    except Exception as e:
        e.args += ('more info',)
        raise

如果你有兴趣了解更多,建议阅读这篇博客。

Python3对重复传递异常有所改进,你可以自己尝试一下,不过建议还是遵循以上规则。

11.Exception 和 BaseException

当我们要捕获一个通用异常时,应该用Exception还是BaseException?我建议你还是看一下 官方文档说明,这两个异常到底有啥区别呢? 请看它们之间的继承关系。

BaseException
 +-- SystemExit
 +-- KeyboardInterrupt
 +-- GeneratorExit
 +-- Exception
      +-- StopIteration...
      +-- StandardError...
      +-- Warning...

Exception的层级结构来看,BaseException是最基础的异常类,Exception继承了它。BaseException除了包含所有的Exception外还包含了SystemExitKeyboardInterruptGeneratorExit三个异常。

由此看来你的程序在捕获所有异常时更应该使用Exception而不是BaseException,因为被排除的三个异常属于更高级别的异常,合理的做法应该是交给Python的解释器处理。

except Exception as e和 except Exception, e

代码示例如下:

try:
    do_something()
except NameError as e:  # should
    pass
except KeyError, e:  # should not
    pass

在Python2的时代,你可以使用以上两种写法中的任意一种。在Python3中你只能使用第一种写法,第二种写法已经不再支持。第一个种写法可读性更好,而且为了程序的兼容性和后期移植的成本,请你果断抛弃第二种写法。

raise "Exception string"

把字符串当成异常抛出看上去是一个非常简洁的办法,但其实是一个非常不好的习惯。

if is_work_done():
    pass
else:
    raise "Work is not done!" # not cool

上面的语句如果抛出异常,那么会是这样的:

Traceback (most recent call last):
  File "/demo/exception_hanlding.py", line 48, in <module>
    raise "Work is not done!"
TypeError: exceptions must be old-style classes or derived from BaseException, not str

这在 Python2.4 以前是可以接受的做法,但是没有指定异常类型有可能会让下游没办法正确捕获并处理这个异常,从而导致你的程序难以维护。简单说,这种写法是是封建时代的陋习,应该扔了。

使用内置的语法范式代替try/except

Python 本身提供了很多的语法范式简化了异常的处理,比如for语句就处理了的StopIteration异常,让你很流畅地写出一个循环。

with语句在打开文件后会自动调用finally并关闭文件。我们在写 Python 代码时应该尽量避免在遇到这种情况时还使用try/except/finally的思维来处理。

# should not
try:
    f = open(a_file)
    do_something(f)
finally:
    f.close()

# should 
with open(a_file) as f:
    do_something(f)

再比如,当我们需要访问一个不确定的属性时,有可能你会写出这样的代码:

try:
    test = Test()
    name = test.name  # not sure if we can get its name
except AttributeError:
    name = 'default'

其实你可以使用更简单的getattr()来达到你的目的。

name = getattr(test, 'name''default')

最佳实践

最佳实践不限于编程语言,只是一些规则和填坑后的收获。

  1. 只处理你知道的异常,避免捕获所有异常然后吞掉它们。
  2. 抛出的异常应该说明原因,有时候你知道异常类型也猜不出所以然。
  3. 避免在catch语句块中干一些没意义的事情,捕获异常也是需要成本的。
  4. 不要使用异常来控制流程,那样你的程序会无比难懂和难维护。
  5. 如果有需要,切记使用finally来释放资源。
  6. 如果有需要,请不要忘记在处理异常后做清理工作或者回滚操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值