Python异常的处理记录

参考资料来至: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__)
[<class 'ConnectionError'>, <class 'BlockingIOError'>, <class 'ChildProcessError'>, <class 'FileExistsError'>, 
<class 'FileNotFoundError'>, <class 'IsADirectoryError'>, <class 'NotADirectoryError'>, <class 'InterruptedError'>,
<class 'PermissionError'>, <class 'ProcessLookupError'>, <class 'TimeoutError'>, <class 'io.UnsupportedOperation'>, <class 'signal.ItimerError'>] (<class 'FileNotFoundError'>, <class 'OSError'>, <class 'Exception'>, <class 'BaseException'>, <class 'object'>)

 书中不严谨的地方事,明显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)
<ipython-input-94-0e9d76aa20ee> 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)
<ipython-input-95-270ee176495d> in <module>
----> 1 example()

<ipython-input-94-0e9d76aa20ee> 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 <class 'ValueError'>

 我们可以从捕获的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)
<ipython-input-98-bdc7d22b895d> 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)
<ipython-input-100-270ee176495d> in <module>
----> 1 example()

<ipython-input-98-bdc7d22b895d> 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)
<ipython-input-103-270ee176495d> in <module>
----> 1 example()

<ipython-input-102-095e6117c755> 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)
<ipython-input-107-270ee176495d> in <module>
----> 1 example()

<ipython-input-106-22d255d51f1f> 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'

 这个以后再工作中,对异常进行采集还是非常有用的,采集了以后再次进行上浮报错。

原文地址:https://www.cnblogs.com/sidianok/p/12229828.html