参考资料来至:PythonCookbook
首先要知道,所有的异常事一个类。
如果一个单独的代码块处理所有不同的异常,可以将它们归组到一个元祖中。
from urllib.error import URLError try: object.get_url(url) except (URLError, ValueError, SocketTimeout): object.remove_url(url)
这个错误元祖(URLError, ValueError, SocketTimeout)捕获到都会执行object.remove_url(url)
如果对一个单独的采取不同的处理方法,可以将其放入一个单独的except中
try: object.get_url(url) except (URLError, ValueError): object.remove_url(url) except SocketTimeout: object.other_do(url)
有许多归组为继承体系,对于这样的异常,可以通过指定一个基类来捕获所有异常。
try: f = open('a.txt') except (FileNotFoundError, PermissionError): ... try: f = open('a.txt') except OSError: ... # 查下此类下面的子类 print(OSError.__subclasses__()) print()
# 查看继承表 print(FileNotFoundError.__mro__)
[, , , , , , , ,
, , , , ] ( , , , , )
书中不严谨的地方事,明显OSError包含的错误类型更加多。
我们还可以通过as e,这个e就是捕获到错误的实例,实例的话就应该有一堆属性,你也可以通过type(e)的方式查看e的类型,便于精准捕获。
其中的error属性可以捕获到错误的代码。
except向下执行捕获到合适的异常,下面的except就不会执行。
try: f = open('a.txt') except OSError as e:
print(dir(e)) print('OSError') # 这个肯定不会执行,应该OSError是FileNotFoundError的父类,它把包含它的所有异常都捕获了 except FileNotFoundError: print('FileNotFoundError')
下面上一下OSError对象的属性
['__cause__', '__class__', '__context__', '__delattr__', '__dict__',
'__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__',
'__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__',
'__subclasshook__', '__suppress_context__', '__traceback__', 'args', 'characters_written', 'errno', 'filename', 'filename2', 'strerror', 'with_traceback']
捕获所有的异常
try: ... # 一般错误这个够用了 except Exception as e: ... # 这个是错误的祖宗 except BaseException as e: ...
除了SystemExit,KeyboardInterrupt,和GeneratorExit之外,Exception都能捕获,如果想前面几个异常也捕获可以用BaseException。
书中介绍说明,当我们捕获错误的时候,如果选择了Exception这样的广义错误,最好用as e,把e输出显示,这样再出现问题的时候,我们可以从实例e的输出中查看具体的情况。
创建自定义的异常
我们日常的使用中需要一些自己定义的异常,简单的操作,可以继承该类,并自己重新定义一个自己熟悉的类名。
实际操作中,我们一般总继承内奸的Exception类,或者继承自一些本地定义的基类,而这个基类本身又是继承自Exception的,不能去继承BaseException。
如果改写Exception的__init__方法,请确保所有参数传递给Exception的__init__,Exception默认的认为就是接收所有传递过来的参数,并将它以元祖的形式保存再args中。
In [82]: class My_Exception(Exception): ...: def __init__(self,message, status): ...: super().__init__(message, status) ...: self.message = message ...: self.status = status ...: In [85]: try: ...: raise My_Exception('my error','888') ...: except My_Exception as e: ...: print(e.message) ...: print(e.status) ...: print(e.args) ...: ...: ...: my error 888 ('my error', '888')
通过引发异常来响应另一个异常。
要将异常串联起来,可以使用yield from。
In [94]: def example(): ...: try: ...: int('n/a') ...: except ValueError as e: ...: raise RuntimeError('A parseing error occurred') from e ...: In [95]: example() --------------------------------------------------------------------------- ValueError Traceback (most recent call last)in example() 2 try: ----> 3 int('n/a') 4 except ValueError as e: ValueError: invalid literal for int() with base 10: 'n/a' The above exception was the direct cause of the following exception: RuntimeError Traceback (most recent call last) in ----> 1 example() in example() 3 int('n/a') 4 except ValueError as e: ----> 5 raise RuntimeError('A parseing error occurred') from e 6 RuntimeError: A parseing error occurred In [96]:
这一行是关键The above exception was the direct cause of the following exception:
在生成器throw(StopItoration)里面引起RuntimeError应该也是采用了yield from的方式。
In [97]: try: ...: example() ...: except RuntimeError as e: ...: print('It didnot work', e) ...: if e.__cause__: ...: print('Cause', e.__cause__,'type',type(e.__cause__)) ...: ...: It didnot work A parseing error occurred Cause invalid literal for int() with base 10: 'n/a' type
我们可以从捕获的RuntimeError对象e里面发现有__cause__属性,调用后,为引起它错误的实例。
In [98]: def example(): ...: try: ...: int('n/a') ...: except ValueError: ...: raise RuntimeError('A parseing error occurred') ...: ...: In [99]: try: ...: example() ...: except RuntimeError as e: ...: print('It didnot work', e) ...: if e.__cause__: ...: print('Cause', e.__cause__,'type',type(e.__cause__)) ...: ...: It didnot work A parseing error occurred
In [100]: example()
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)in example()
2 try:
----> 3 int('n/a')
4 except ValueError:
ValueError: invalid literal for int() with base 10: 'n/a'
During handling of the above exception, another exception occurred:
RuntimeError Traceback (most recent call last)in
----> 1 example()in example()
3 int('n/a')
4 except ValueError:
----> 5 raise RuntimeError('A parseing error occurred')
6
7
RuntimeError: A parseing error occurred
上面去掉了yield from 可以发现两个错误没有因果关系,调用__cause__属性为空.
在except里面无论是否是raise还是raise from,该脚本的调用者,抓取异常以最后复现的异常为准。就像前面写的函数,最后抓取函数异常,只能通过RuntimeError抓取。
如果处于某种元婴想阻止异常链的发生,可以使用raise from None 完成
In [103]: example() --------------------------------------------------------------------------- RuntimeError Traceback (most recent call last)in ----> 1 example() in example() 3 int('n/a') 4 except ValueError: ----> 5 raise RuntimeError('A parseing error occurred') from None 6 7 RuntimeError: A parseing error occurred
在设计代码的时候,如果需要再except中raise 错误,经量应该使用raise from,这样能够明确的表达处你希望引发第二个异常的意图。
少出现直接except下直接raise 下一个错误。
重新抛出上一个异常
我们在except块中捕获了一个异常,现在想将它重新抛出,可以再except中重新使用raise。
In [106]: def example(): ...: try: ...: int('a') ...: except ValueError as e: ...: print(e) ...: raise ...: In [107]: example() invalid literal for int() with base 10: 'a' --------------------------------------------------------------------------- ValueError Traceback (most recent call last)in ----> 1 example() in example() 1 def example(): 2 try: ----> 3 int('a') 4 except ValueError as e: 5 print(e) ValueError: invalid literal for int() with base 10: 'a'
这个以后再工作中,对异常进行采集还是非常有用的,采集了以后再次进行上浮报错。