python场景异常_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中的处理异常,可以把异常都收集起来到一个全局变量,最后程序退出的时候,把所有的异常都打印出来。

总结:兼容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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值