Python中raise…from用法浅析
本来这几天是计划阅读string模块的源码,恰好其中一段异常处理的代码我觉得很新奇,也就是raise…from的用法,raise的用法大家都知道。因为我之前没遇到过,所以就去网上查了相关的资料,自己试验一下。
我阅读的这段代码取自于string模块的Formatter类的format方法,源码如下:
class Formatter: def format(*args, **kwargs): if not args: raise TypeError("descriptor 'format' of 'Formatter' object " "needs an argument") self, *args = args try: format_string, *args = args except ValueError: if 'format_string' in kwargs: format_string = kwargs.pop('format_string') import warnings warnings.warn("Passing 'format_string' as keyword argument is " "deprecated", DeprecationWarning, stacklevel=2) else: raise TypeError("format() missing 1 required positional " "argument: 'format_string'") from None return self.vformat(format_string, args, kwargs)
在异常捕获的else分支中,用到了raise…from的语法。而且这里比较特别的是raise…from None。我之前一直不知道这样的用法,所以就自己试验了一下。
例如我们直接用两段代码来对比一下。
try: raise IndexError except Exception as e: raise ValueError
这里就是我们常见的异常捕获的写法,不过在下面的捕获中又引发了一个新的异常,报错信息如下。
Traceback (most recent call last):
File “E:/pythonlab/test127.py”, line 2, in <module>
raise IndexError
IndexError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File “E:/pythonlab/test127.py”, line 4, in <module>
raise ValueError
ValueError
try: raise IndexError except Exception as e: raise ValueError from e
再用raise…from的语法试试。
Traceback (most recent call last):
File “E:/pythonlab/test127.py”, line 2, in <module>
raise IndexError
IndexError
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File “E:/pythonlab/test127.py”, line 4, in <module>
raise ValueError from e
ValueError
对比发现其实差别在于提示信息的不同
- During handling of the above exception, another exception occurred:
- The above exception was the direct cause of the following exception:
前一个是 在处理上述异常期间,发生了另一个异常。
后者为 上述异常是以下异常的直接原因。
try: raise IndexError except Exception as e: raise ValueError from None
而如果将其改为from None,那么报错如下。
Traceback (most recent call last):
File “E:/pythonlab/test127.py”, line 4, in <module>
raise ValueError from None
ValueError
此时报错提示信息也简洁了很多。
现在再分析一下其原理。
当在 except 块或者 finally 块中出现异常时(包括使用单独的 raise 重新抛出异常的情况),之前的异常会被附加到新异常的 __context__ 属性上。
而在其他任何地方抛出异常,都不会设置 __context__ 属性。
这样打印出来的异常信息就会包含这么一句话:During handling of the above exception, another exception occurred:。
raise A from B
raise A from B 语句用于连锁 chain 异常。
from 后面的 B 可以是:
- 异常类
- 异常实例
- None(Python 3.3 的新特性)
如果 B 是异常类或者异常实例,那么 B 会被设置为 A 的 __cause__ 属性,表明 A异常 是由 B异常 导致的。
这样打印出来的异常信息就会包含这样一句话:The above exception was the direct cause of the following exception:。
与此同时,在 Python 3.3 中 A异常 的 __suppress_context__ 属性会被设置为 True,这样就抑制了 A异常 的 __context__ 属性,即忽略 __context__ 属性。
于是 Python 就不会自动打印异常上下文 exception context,而是使用 __cause__ 属性来打印异常的引发者。
在 Python 3.3 中,B 还可以是 None:raise A异常 from None。
这样相当于把 __suppress_context__ 属性设置为 True,从而禁用了 __context__ 属性,Python 不会自动展示异常上下文。
下面是Python中所有异常的基类BaseException类的代码。
class BaseException(object): """ Common base class for all exceptions """ def with_traceback(self, tb): # real signature unknown; restored from __doc__ """ Exception.with_traceback(tb) -- set self.__traceback__ to tb and return self. """ pass def __delattr__(self, *args, **kwargs): # real signature unknown """ Implement delattr(self, name). """ pass def __getattribute__(self, *args, **kwargs): # real signature unknown """ Return getattr(self, name). """ pass def __init__(self, *args, **kwargs): # real signature unknown pass @staticmethod # known case of __new__ def __new__(*args, **kwargs): # real signature unknown """ Create and return a new object. See help(type) for accurate signature. """ pass def __reduce__(self, *args, **kwargs): # real signature unknown pass def __repr__(self, *args, **kwargs): # real signature unknown """ Return repr(self). """ pass def __setattr__(self, *args, **kwargs): # real signature unknown """ Implement setattr(self, name, value). """ pass def __setstate__(self, *args, **kwargs): # real signature unknown pass def __str__(self, *args, **kwargs): # real signature unknown """ Return str(self). """ pass args = property(lambda self: object(), lambda self, v: None, lambda self: None) # default __cause__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default """exception cause""" __context__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default """exception context""" __suppress_context__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default __traceback__ = property(lambda self: object(), lambda self, v: None, lambda self: None) # default __dict__ = None # (!) real value is ''
这样就可以理解得更加深刻些了。引用网上的总结。
在异常处理程序或 finally
块中引发异常,Python 会为异常设置上下文,可以手动通过 with_traceback()
设置其上下文,或者通过 from
来指定异常因谁引起的。这些手段都是为了得到更友好的异常回溯信息,打印清晰的异常上下文。若要忽略上下文,则可以通过 raise ... from None
来禁止自动显示异常上下文。
虽然我暂时不清楚他实际的用处在哪,不过在知乎中我看到一位网友的说到,感觉以后肯定会用到的,就先mark吧。
参考文章:
https://zhuanlan.zhihu.com/p/52091476
https://blog.csdn.net/jpch89/article/details/84315444
赞赏微信赞赏支付宝赞赏
发表评论