【www.bbyears.com--python】
异常对于一个语言来说非常重要,异常的栈信息对于开发者特别重要,因为可以根据异常栈来找到第一次抛出异常的地方。但是懂得正确的抛出异常的人不是很多。
首先,以下是最糟糕的
def revert_stuff():
pass
def some_code():
raise Exception("oops, some error occur")
try:
some_code()
except:
revert_stuff()
raise Exception("some_code failed!")
Traceback (most recent call last):
File "1.py", line 11, in
raise Exception("some_code failed!")
Exception: some_code failed!
为什么说是最糟糕呢?因为关于some_code()的失败信息全部都丢失了。不过这些异常堆栈,可能是我们期望的信息,也可能不是。
以下代码是稍微改进过,但还是不是非常好:
def revert_stuff():
pass
def some_code():
raise Exception("oops, some error occur")
try:
some_code()
except:
import traceback
traceback.print_exc()
revert_stuff()
raise Exception("some_code failed!")
Traceback (most recent call last):
File "2.py", line 8, in
some_code()
File "2.py", line 5, in some_code
raise Exception("oops, some error occur")
Exception: oops, some error occur
Traceback (most recent call last):
File "2.py", line 13, in
raise Exception("some_code failed!")
Exception: some_code failed!
使用traceback.print_exc() 把原始的异常堆栈(traceback)打印了出来。从某些角度来看这是最好的方法了,因为可以异常的错误信息找出来。但是如果不想恢复着新异常信息,那么应该这么做:
def revert_stuff():
pass
def some_code():
raise Exception("oops, some error occur")
try:
some_code()
except:
revert_stuff()
raise
Traceback (most recent call last):
File "3.py", line 8, in
some_code()
File "3.py", line 5, in some_code
raise Exception("oops, some error occur")
Exception: oops, some error occur
使用不用带任何参数raise可以重新抛出最后的异常。有时人们直接留空从来不用except:语句,但是这个特殊的形式(except: + raise)也是可以的。
有另外一种raise抛出异常的方式,知道的人比较少,但是也很容易上手。类似无参数的raise一样,这种方法也可以保留异常堆栈:
def some_code():
raise Exception("oops, some error occur")
def maybe_raise(exc_info):
raise exc_info[0], exc_info[1], exc_info[2]
try:
some_code()
except:
import sys
exc_info = sys.exc_info()
maybe_raise(exc_info)
Traceback (most recent call last):
File "4.py", line 12, in
maybe_raise(exc_info)
File "4.py", line 8, in
some_code()
File "4.py", line 2, in some_code
raise Exception("oops, some error occur")
Exception: oops, some error occur
如果你需要在异常发生的代码的其他地方处理异常是个不错的选择。但通常它不是很方便,因为这样比较晦涩难懂。
还有一类经常修改异常堆栈的情况是:想加一些额外的信息到异常堆栈中。
for lineno, line in enumerate(file):
try:
process_line(line)
except Exception, exc:
raise Exception("Error in line %s: %s" % (lineno, exc))
这里保留了异常信息,但却丢失了异常堆栈。有一个方法可以保留异常堆栈。下面的方法不仅可以保留异常,也可以改变异常的信息。
except Exception, exc:
args = exc.args
if not args:
arg0 = ""
else:
arg0 = args[0]
arg0 += " at line %s" % lineno
exc.args = arg0 + args[1:]
raise
有些小尴尬。从技术上讲(虽然它不建议使用),你可以抛出任何的异常。如果使用except Exception:那么就不能捕获一些例如string异常或者其他异常。想要这么用取决于是否关心这些场景。不过异常也可能没有.args属性,或者异常的字符串信息不能从这些参数中获取,又或者有其他的方法展示(例如KeyError信息有些不同 ) 。因此这不是万能的。想要增强版,可以这么做:
except:
exc_class, exc, tb = sys.exc_info()
exc_class是一个字符串类型,不过可能有人会raise "not found"。所以这种风格被废弃了。如果坚持想去把周围的东西搞的一团糟,可以这么用:
new_exc = Exception("Error in line %s: %s"
% (lineno, exc or exc_class))
raise new_exc.__class__, new_exc, tb
这样改变了异常类周围使得它变的混乱了,但至少保留完好异常堆栈。在异常堆栈中raise ValueError(...)或者在错误信息raise Exception可能看上去有些奇怪。
小结:Python2中的较佳方法
以下是在Python2中一个较佳重新抛出异常的方法。
try:
code()
except:
exc_info = sys.exc_info()
try:
revert_stuff()
except:
# If this happens, it clobbers exc_info, which is why we had
# to save it above
import traceback
print >> sys.stderr, "Error in revert_stuff():"
traceback.print_exc()
raise exc_info[0], exc_info[1], exc_info[2]
在with语句中抛出异常
如果想在某些场景下保留异常,有些场景下又想不捕获异常,那么可以怎么做
class ignore_or_reraise_exception(object):
def __init__(self, reraise=True):
self.reraise = reraise
def __enter__(self):
self.type_, self.value, self.tb, = sys.exc_info()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is not None:
if self.reraise:
log.error("Original exception being dropped: %s" % self.value)
if self.reraise:
return True # reraise exception
return False # ignore exception
....
except Exception:
with ignore_or_reraise_exception(reraise=False) as ctxt:
if statements to determine whether to raise a new exception:
# Not raising a new exception, so reraise
ctxt.reraise = True
通过这个方法,可以自由的选择是忽略异常,还是想继续抛出异常。当需要抛出异常的时候,指定属性ctxt.reraise = True即可。
另外可以在ignore_or_reraise_exception中的处理异常,可以把异常都收集起来到一个全局变量,最后程序退出的时候,把所有的异常都打印出来。
总结:www.111cn.net兼容Python2、Python3的重新抛出异常方法
在Python2中,重新抛出异常是用raise exc_info[0], exc_info[1], exc_info[2]这样的三个参数。但在Python3中却不一样了。
这个时候只能使用兼容Python2、Python3的类库six了。
使用方法
def some_code():
raise Exception("oops, some error occur")
class SwaggerValidationError(Exception):
pass
try:
some_code()
except Exception as e:
import six, sys
six.reraise(
SwaggerValidationError,
SwaggerValidationError(str(e)),
sys.exc_info()[2])
Traceback (most recent call last):
File "5.py", line 14, in
sys.exc_info()[2])
File "5.py", line 8, in
some_code()
File "5.py", line 2, in some_code
raise Exception("oops, some error occur")
__main__.SwaggerValidationError: oops, some error occur