[Python]异常处理技术(一)

时间:2021-09-07 00:21:00

原文链接(原文比较长,分成2个部分)

通过抛出异常对错误进行打印输出和处理是Python的一项重要特性。不像C语言通常通过函数返回值来打印错误,每次调用的时候都需要进行检查,在Python中,程序员可以在在程序中的任何一个地方引起一个异常。当一个异常被引起的时候,程序被中断同时解释器会使用异常处理器返回堆中找到当时的上下文信息。这种查找的算法允许错误处理被集中到一起或者是更高一层的地方,使得程序结构非常清晰。函数库或许根本不需要做任何的异常处理了,简单的脚本就不需要把异常处理的代码块放在主文件中也能打印出漂亮的错误信息了。适当的异常处理在更复杂的情况下是很机警的,可是,在一些特殊的情况下当异常传回堆中的时候程序自身必须要清除自己。(@没怎么理解)


抛出和捕获 Throwing and Catching

这些声明通常用来处理异常的引起和除去。raise, except都是Python的关键字。最普通的做法就是使用一个exception类的实例引起异常并抛出。
#!/usr/bin/env python

def throws():
raise RuntimeError('this is the error message')

def main():
throws()

if __name__ == '__main__':
main()

exception不同需要的参数也不同,但是通常都包含了用来阐述发生什么问题的一个字符串信息。如果没有写exception的处理,默认的处理是解释器会把这个异常所有的错误回溯和错误信息全部打印出来。

$ python throwing.py
Traceback (most recent call last):
File "throwing.py", line 10, in <module>
main()
File "throwing.py", line 7, in main
throws()
File "throwing.py", line 4, in throws
raise RuntimeError('this is the error message')
RuntimeError: this is the error message

对于很多脚本这种处理已经最够用了,但是更好的做法是捕获异常和打印一个有好的错误信息。

#!/usr/bin/env python

import sys

def throws():
raise RuntimeError('this is the error message')

def main():
try:
throws()
return 0
except Exception, err:
sys.stderr.write('ERROR: %s\n' % str(err))
return 1

if __name__ == '__main__':
sys.exit(main())

上面所有的例子,所有的都是Exception派生的异常被捕获,仅仅是把错误信息打印或者是标准输出。程序顺承unix的惯例,返回一个退出码表明是否是一个错误。

$ python catching.py
ERROR: this is the error message


Logging Exceptions   用日志记录异常

对于守护进程和其他后台进程,直接打印到标准错误输出不是一个好的选择。文件描述符可能被关闭,或被重定向到其他地方导致错误很难被发现。更好的选择的是使用logging模块记录错误信息,包括全部的错误堆栈信息。

#!/usr/bin/env python

import logging
import sys

def throws():
raise RuntimeError('this is the error message')

def main():
logging.basicConfig(level=logging.WARNING)
log = logging.getLogger('example')
try:
throws()
return 0
except Exception, err:
log.exception('Error from throws():')
return 1

if __name__ == '__main__':
sys.exit(main())

在这个例子中,logger使用的是默认的配置,直接把错误打印到标准错误输出,这个很容易调整。把追溯信息保存到一个日志文件中更容易对那些在生产环境以外的地方难以重现的问题进行调试。

$ python logging_errors.py
ERROR:example:Error from throws():
Traceback (most recent call last):
File "logging_errors.py", line 13, in main
throws()
File "logging_errors.py", line 7, in throws
raise RuntimeError('this is the error message')
RuntimeError: this is the error message

Cleaning Up and Re-raising  清除和再次引起异常

在很多程序中,简单的打印出异常是不够的。如果一个错误出现在一个很长处理过程中的一个环节中,你可能要撤消掉一些已经完成的工作。例如,数据库的一些回滚操作和临时文件的清除工作。有两种方法可以处理清理操作,使用finally关键字处理异常,或者是在清理操作完成之后再引起一个明确的异常。

对于那些必须执行的清除操作,最简单的方法就是使用 try:finally语句块。finally块里面的操作一定会被执行,甚至try块中的语句出现了异常。

#!/usr/bin/env python

import sys

def throws():
print 'Starting throws()'
raise RuntimeError('this is the error message')

def main():
try:
try:
throws()
return 0
except Exception, err:
print 'Caught an exception'
return 1
finally:
print 'In finally block for cleanup'

if __name__ == '__main__':
sys.exit(main())

老式风格的例子都是封装一个try:except块在一个try:finall块中,用来保证清除的代码在主程序出现任何情况的时候都可以执行。

$ python try_finally_oldstyle.py
Starting throws()
Caught an exception
In finally block for cleanup
虽然你还可能看到那种老式风格的代码,但是从Python2.5 try:except 和try:finally块就合并为一层级了。因为这种新的风格使用了更少的缩进,更加容易读懂,很快就被接受了。

#!/usr/bin/env python

import sys

def throws():
print 'Starting throws()'
raise RuntimeError('this is the error message')

def main():
try:
throws()
return 0
except Exception, err:
print 'Caught an exception'
return 1
finally:
print 'In finally block for cleanup'

if __name__ == '__main__':
sys.exit(main())
输出的结果和老式风格的相同:

$ python try_finally.py
Starting throws()
Caught an exception
In finally block for cleanup