异常捕获
try...except...finally
Python支持异常处理,最简单的捕获异常的方式就是try...except...finally字句,
try:
1 / 0
except:
print 'except'
finally:
print 'finally'
except字句中不指定类型时意味着捕获所有类型异常,也可以在其中指定具体异常类型,
try:
1 / 0
except ZeroDivisionError as e:
print 'except', e
多个异常可以在不同except子句中进行捕获,也可以在一个except字句中处理多种类型异常,
try:
1 / 0
except ZeroDivisionError as e:
print 'except', e
except IOError as e:
print 'except', e
try:
1 / 0
except (ZeroDivisionError, IOError) as e:
print 'except', e
try...except...else
Python还支持try...except...else语句,当没有指定异常抛出的时候,else中的内容就会被执行,
try:
1 / 1
except ZeroDivisionError as e:
print 'except', e
else:
print 'no exception'
with
Python中的with子句可以简化频繁出现的try...except...finally处理,最常见的如打开文件的相关处理,
with open('input.txt') as inputs:
pass
__enter__, __exit__
想使用with字句,可以通过实现context manager type协议来支持with字句,
contextmanager.__enter__()
contextmanager.__exit__(exc_type, exc_val, exc_tb)
一个简单的例子,
class Foobar(object):
def __enter__(self):
print '__enter__'
def __exit__(self, exc_type, exc_val, exc_tb):
print '__exit__', exc_type, exc_val, exc_tb
return True
with Foobar() as foobar:
1 / 0
如上__enter__函数在进入with子句时会被调用,__exit__在退出时调用,如果存在异常,则异常信息通过参数传入。__exit__ 需要返回bool值用以表明异常是否在函数内已经被处理。
contextlib
Python还提供了contextlib以简化with子句的定义,使用contextlib.contextmanager,
from contextlib import contextmanager
@contextmanager
def foobar(msg):
print 'start'
try:
yield msg
except:
print 'except'
finally:
print 'finally'
print 'end'
with foobar("foobar") as name:
print 'contextmanager'
被contextmanager修饰的函数需要返回generator。
异常内容展示
sys & traceback
当异常对象被捕获后,一个自然的需求就是输出异常的调用栈,但是Python并未提供类似Java中的printStackTrace函数。这个功能需要我们自己去实现。
sys.exc_info可以返回最新的异常信息,traceback模块提供了格式化函数,
try:
1 / 0
except:
print '\n'.join(traceback.format_exception(*sys.exc_info()))
如果只是格式化最新的异常,也可以直接调用traceback.format_exc方法,
try:
1 / 0
except:
print traceback.format_exc()
traceback上还提供获取当前调用栈的功能,在无需显示抛出异常的情况下,也可以清除的展示函数调用关系,
print '\n'.join(traceback.format_stack())
inspect
通过sys & traceback上提供的方法,我们能获取到异常调用栈,不过很多时候我们会希望能够获取当更多的信息,比如函数调用过程中传递的参数、局部变量值等等,这些对解决异常排查问题很有帮助。通过inspect上的方法,可以获取当上述这些内容。
def get_detail_stack_info():
stack_list = inspect.stack()[1:]
stack_list.reverse()
result = []
for idx, item in enumerate(stack_list):
frame, path, line, name, code, num = item
info = {
"path": path,
"line": line,
"name": name,
"code": '.'.join(['\t' + x.strip() for x in code]),
"args": inspect.formatargvalues(*inspect.getargvalues(frame))
}
result.append('File: "{path}", line {line}, in {name}\n{code}, args: {args}\n'.format(**info))
return result
inspect.stack可以获取到调用栈,在这之后在通过相关接口从frame上获取到函数调用的参数信息。
sys.excepthook
Java Thread类提供了setUncaughtExceptionHandler方法用以处理没有没捕获的异常。
public void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
Python也提供了相近的功能,这个功能存在于sys.excepthook。excepthook定义如下,
sys.excepthook(type, value, traceback)
我们可以自定义具备相同函数签名的函数,替换掉默认的excepthook,
def default_exception_hook(exp_type, exp_value, exp_traceback):
exp_info = '\n'.join(traceback.format_exception(exp_type, exp_value, exp_traceback))
print exp_info
sys.excepthook = hook_func
1 / 0
总结
Python的异常处理与Java之类有不少相似之处,不过就接口封装而言,Python的有些过于分散。上面只是介绍了如果捕获到异常,获取到异常消息之后根据具体的需求可以进一步再处理,比如将异常消息写入日志、通过异常收集系统收集异常信息等等,有机会再来记录后续这些方法吧。