全套异常捕获与处理
异常
异常即非正常状态,在Python中使用异常对象来表示异常。若程序在编译或运行过程中发生错误,程序的执行过程就会发生改变,抛出异常对象,程序流进入异常处理。如果异常对象没有被处理或捕捉,程序就会执行回溯(Traceback)来终止程序。
异常类型
通用异常类型表
异常 | 描述 |
---|---|
BaseException | 所有异常的基类 |
SystemExit | 解释器请求退出 |
KeyboardInterrupt | 用户中断执行(通常是输入^C) |
Exception | 常规错误的基类 |
StopIteration | 迭代器没有更多的值 |
GeneratorExit | 生成器(generator)发生异常来通知退出 |
StandardError | 所有的内建标准异常的基类 |
ArithmeticError | 所有数值计算错误的基类 |
FloatingPointError | 浮点计算错误 |
OverflowError | 数值运算超出最大限制 |
ZeroDivisionError | 除(或取模)零 (所有数据类型) |
AssertionError | 断言语句失败 |
AttributeError | 对象没有这个属性 |
EOFError | 没有内建输入,到达EOF 标记 |
EnvironmentError | 操作系统错误的基类 |
IOError | 输入/输出操作失败 |
OSError | 操作系统错误 |
WindowsError | 系统调用失败 |
ImportError | 导入模块/对象失败 |
LookupError | 无效数据查询的基类 |
IndexError | 序列中没有此索引(index) |
KeyError | 映射中没有这个键 |
MemoryError | 内存溢出错误(对于Python 解释器不是致命的) |
NameError | 未声明/初始化对象 (没有属性) |
UnboundLocalError | 访问未初始化的本地变量 |
ReferenceError | 弱引用(Weak reference)试图访问已经垃圾回收了的对象 |
RuntimeError | 一般的运行时错误 |
NotImplementedError | 尚未实现的方法 |
SyntaxError | Python 语法错误 |
IndentationError | 缩进错误 |
TabError | Tab 和空格混用 |
SystemError | 一般的解释器系统错误 |
TypeError | 对类型无效的操作 |
ValueError | 传入无效的参数 |
UnicodeError | Unicode 相关的错误 |
UnicodeDecodeError | Unicode 解码时的错误 |
UnicodeEncodeError | Unicode 编码时错误 |
UnicodeTranslateError | Unicode 转换时错误 |
Warning | 警告的基类 |
DeprecationWarning | 关于被弃用的特征的警告 |
FutureWarning | 关于构造将来语义会有改变的警告 |
OverflowWarning | 旧的关于自动提升为长整型(long)的警告 |
PendingDeprecationWarning | 关于特性将会被废弃的警告 |
RuntimeWarning | 可疑的运行时行为(runtime behavior)的警告 |
SyntaxWarning | 可疑的语法的警告 |
UserWarning | 用户代码生成的警告 |
Exception类:是通用异常基类下列异常类均继承于Exception类,Python解析器会自动将通用异常类型名称放在内建命名空间中,所以当使用通用异常类型时,不需要import exceptions模块。
异常处理
触发异常raise
raise关键字:手动抛出一个通用的异常类型(Exception),类似Java中的throw语句。raise关键字后跟异常的名称,异常名称能够标识出异常类的对象。执行raise语句时,python会创建指定异常类的对象,还能够指定对异常对象进行初始化的参数,参数也可以为由若干参数组成的元组。
注意:一旦执行raise语句,程序就会被终止。
格式:raise [exceptionType[,argument][,traceback]]
def testRaise(number):
if number < 1:
raise ValueError('Invalid value') #或者 raise ValueError,'Invalid value'
testRaise(0)
- 1
- 2
- 3
- 4
- 5
traceback:这个参数用于追踪异常对象,一般很少使用。
这样就可以触发一个异常,并且接收异常信息。
传递异常
当你捕获到异常之后又希望再次的触发异常只需要使用不带任何参数的raise关键字。
!/usr/bin/env python
import os
try:
openFile = open('notExistsFile.txt','r')
fileContent = openFile.readlines()
except IOError:
print 'File not Exists'
if not os.path.exists('notExistsFile.txt'):
raise
except:
print 'process exception'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
异常会在捕获之后再次触发同一个异常。
assert语句触发异常
assert语句根据后面的表达式的真假来控制程序流。若为True,则往下执行。若为False,则中断程序并调用默认的异常处理器,同时输出指定的提示信息。
格式:
assert expression,'information'
- 1
Example:
#!/usr/bin/env python
def testAssert(x):
assert x < 1,'Invalid value'
testAssert(1)
print 'Valid value'
- 1
- 2
- 3
- 4
- 5
output:
AssertionError: Invaild value
- 1
捕获异常try..except..else
注意:except子句的数量没有限制,但使用多个except子句捕获异常时,如果异常类之间具有继承关系,则子类应该写在前面,否则父类将会直接截获子类异常。放在后面的子类异常也就不会执行。
格式:
try:
可能触发异常的语句块
except [exceptionType]:
捕获可能触发的异常[可以指定处理的异常类型]
except [exceptionType][,date]:
捕获异常并获取附加数据
except:
没有指定异常类型,捕获任意异常
else:
没有触发异常时,执行的语句块
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
try的工作原理:
执行一个try语句时,python解析器会在当前程序流的上下文中作标记,当出现异常后,程序流能够根据上下文的标记回到标记位,从而避免终止程序。
1. 如果try语句执行时发生异常,程序流跳回标记位,并向下匹配执行第一个与该异常匹配的except子句,异常处理完后,程序流就通过整个try语句(除非在处理异常时又引发新的异常)。
2. 如果没有找到与异常匹配的except子句(也可以不指定异常类型或指定同样异常类型Exception,来捕获所有异常),异常被递交到上层的try(若有try嵌套时),甚至会逐层向上提交异常给程序(逐层上升直到能找到匹配的except子句。实在没有找到时,将结束程序,并打印缺省的错误信息)。
3. 如果在try子句执行时没有发生异常,python将执行else语句后的语句(可选),然后控制流通过整个try语句。
#!/usr/bin/env python
try:
openFile = open('notExistsFile.txt','r')
fileContent = openFile.readlines()
except IOError:
print 'File not Exists' #执行
except:
print 'process exception' #不执行
else:
print 'Reading the file' #不执行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
output:
In [157]: %run testError.py
File not Exists
- 1
- 2
嵌套try:
#!/usr/bin/env python
try:
try:
openFile = open('notExistsFile.txt','r')
fileContent = openFile.readlines()
except IOError:
print 'File not Exists' #执行
except:
print 'process exception' #不执行
else:
print 'Reading the file' #执行
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
Output:
In [159]: %run testError.py
File not Exists
Reading the file
- 1
- 2
- 3
捕捉多个异常
方法一:指定一个通用异常,可以捕获多个不同的包含在Exception类中的异常类。
try:
语句块
except Exception:
语句块
- 1
- 2
- 3
- 4
方法二:在一个except子句后将多个异常作为元组元素列出。
try:
语句块
except (IOError,ValueError):
语句块
- 1
- 2
- 3
- 4
方法三:except子句后不带任何异常名称,捕获所有异常
try:
语句块
except:
语句块
- 1
- 2
- 3
- 4
try..finally语句
无论try语句块中是否触发异常,都会执行finally子句中的语句块,因此一般用于关闭文件或关闭因系统错误而无法正常释放的资源。比如文件关闭,释放锁,把数据库连接返还给连接池等。
import os
def write_test(fileName,content_iterable):
try:
pwd = open(fileName,'w')
for key,value in content_iterable.items():
pwd.write(key+'\t'+value+'\n') #传入String类型参数同时加入换行符
finally:
pwd.close()
if __name__ == '__main__':
fileName = '/usr/local/src/pyScript/fileOperation.txt'
dic = {'name':'Jmilk','age':'23','city':'BJ'}
if os.path.exists(fileName):
write_test(fileName,dic)
else:print 'File not exist!'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
注意:try..finally与try..except 是可以同时使用的。
In [3]: try:
...: raise
...: except Exception:
...: print 'error'
...: raise
...: finally:
...: print 'success'
...:
error
success
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-530db52949e7> in <module>()
1 try:
----> 2 raise
3 except Exception:
4 print 'error'
5 raise
TypeError: exceptions must be old-style classes or derived from BaseException, not NoneType
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
NOTE:try…finally 的意义在于,就是我们在 try 代码块中执行了 return 语句,但是仍然会继续执行在 finally 中的代码块,所以我们一般用作处理资源的释放。
自定义异常
通过(直接或简介)继承Exception类来创建一个自定义异常类,自定义的异常类只能通过raise关键字来手动触发。
class testError(Exception): #直接集成Exception类
def __init__(self,arg):
self.args = arg
try:
raise testError('Just test')
except testError,info:
print info.args
- 1
- 2
- 3
- 4
- 5
- 6
- 7
output:
In [52]: %run test.py
('J', 'u', 's', 't', ' ', 't', 'e', 's', 't')
- 1
- 2
with..as触发异常自动关闭资源
在使用类文件的流对象时,都需要单独的调用close()来关闭资源。with..as语句能够实现在with语句块执行完后,自动的关闭文件。如果with语句块中触发异常,会调用默认的异常处理器处理,而且文件仍然能够正常关闭。
#!/usr/bin/env python
import os
def testWith(fileName):
try:
with open(fileName,'r+') as pwd:
pwd.readlines()
print 2/0
except Exception:
print 'File closed:',pwd.closed #判断文件是否关闭
if __name__ == '__main__':
if os.path.exists('/usr/local/src/pyScript/fileOperation.txt'):
testWith('/usr/local/src/pyScript/fileOperation.txt')
print 'continue'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
output:
In [17]: %run test.py
File closed: True #没有call close()函数,文件仍然自动关闭。
continue
- 1
- 2
- 3
as获取异常信息
每个异常都会有一定的描述信息,可以通过as关键字来获取。但是这种异常信息并不适合一般用户阅读,所以会使用自定义的异常信息。但是仍然会将原有的异常信息保留起来,用于后期的异常分析。
#!/usr/bin/env python
try:
try:
openFile = open('notExistsFile.txt','r')
fileContent = openFile.readlines()
except (IOError,ValueError) as info: #或者except (IOError,ValueError),info:
print info
except:
print 'process exception'
else:
print 'Reading the file'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
output:
In [164]: %run testError.py
[Errno 2] No such file or directory: 'notExistsFile.txt'
Reading the file
- 1
- 2
- 3
异常参数
也可以使用异常参数作为输出的异常信息参数,来获取异常信息。并且异常参数中包含有异常信息、错误数字、错误位置等属性。
#!/usr/bin/env python
try:
try:
openFile = open('notExistsFile.txt','r')
fileContent = openFile.readlines()
except (IOError,ValueError),info:
print dir(info)
print info.args
except:
print 'process exception'
else:
print 'Reading the file'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
output:
In [44]: %run test.py
['__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']
(2, 'No such file or directory')
Reading the file
- 1
- 2
- 3
- 4
- 5
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()函数来获取相同的信息。
#!/usr/bin/env python
import traceback
try:
openFile = open('notExistsFile.txt','r')
fileContent = openFile.readlines()
except IOError as info:
print 'File not Exists'
print info
traceback.print_exc()
print 'continue'
except:
print 'process exception'
else:
print 'Reading the file'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
output:
In [38]: %run test.py
File not Exists
[Errno 2] No such file or directory: 'notExistsFile.txt'
Traceback (most recent call last):
File "/usr/local/src/pyScript/test.py", line 5, in <module>
openFile = open('notExistsFile.txt','r')
IOError: [Errno 2] No such file or directory: 'notExistsFile.txt'
continue
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
异常信息的重定向:如果希望将异常的信息保存在一个指定的文件中,以供后期分析。可以使用下面的方法:
#!/usr/bin/env python
import traceback
try:
with open('notExistsFile.txt','r') as openFile:
fileContent = openFile.readlines()
except IOError:
with open('errorLog','w+') as errorInfo:
traceback.print_exc(file=errorInfo)
print 'continue'
except:
print 'process exception'
else:
print 'Reading the file'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
output:
In [61]: %run test.py
continue
In [62]: cat errorLog
Traceback (most recent call last):
File "/usr/local/src/pyScript/test.py", line 5, in <module>
with open('notExistsFile.txt','r') as openFile:
IOError: [Errno 2] No such file or directory: 'notExistsFile.txt'
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
sys.exc_info()获取异常信息
traceback.print_exc()函数实际上是call sys.exc_info()
import sys
try:
a=b
b=c
except:
info=sys.exc_info()
print info[0],":",info[1]
- 1
- 2
- 3
- 4
- 5
- 6
- 7
output:
In [65]: %run test.py
<type 'exceptions.NameError'> : name 'b' is not defined
- 1
- 2
最后
异常处理用于处理程序错误之外,还有许多应用的地方。如关闭资源、平台兼容、模块导入等。这些使用都是基于对异常处理的实现和机制的理解上,以后我们再一起学习。