Python错误处理和异常处理(二)

发表时间:2020-02-18

前面我们讲到Python编程过程中,在可能出现异常的地方使用try语句,来正确的处理一些异常,可以保证程序不中断继续运行。

错误和异常

抛出异常

有时候,我们可能需要在程序的某些地方主动抛出异常,通知调用该代码的程序有错误发生。这时候,我们就要用到 raise 语句。 raise 语句就是帮助我们抛出知道异常的,比如:

In [6]: raise NameError("Bad Name")
-----------------------------------
NameError           Traceback (most recent call last)
<ipython-input-6-966a00c8f456> in <module>
----> 1 raise NameError("Bad Name")

NameError: Bad Name

raise 的使用很简单,它的语法如下:

raise [expression [from expression]]

如果它后面不带表达式(参数),它会重新引发当前作用域内最后一个激活的异常。 如果当前作用域内没有激活的异常,将会引发 RuntimeError 来提示错误。

如果后面带有表达式,则将表达式求值为要抛出的异常对象,该表达式必须是一个异常实例或者是一个异常类(继承自 BaseException 类)。如果它是一个异常类,它将通过调用没有参数的构造函数来隐式实例化:

raise NameError  # 等同于 'raise NameError()'

raise 表达式后面还可以跟一个 from 子句,用于异常的串联。 from 子句的表达式必须是另一个异常或实例,它将作为可写的(writable)的 __cause__ 属性被关联到所引发的异常。如果引发的异常未被捕捉处理,两个异常都将被打印出来:

In [9]: try: 
   ...:     print(10/0) 
   ...: except Exception as e: 
   ...:     raise RuntimeError("something is wrong") from e 
   ...:
----------------------------------------------------------
ZeroDivisionError            Traceback (most recent call last)
<ipython-input-9-7de64aad634f> in <module>
      1 try:
----> 2     print(10/0)
      3 except Exception as e:

ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

RuntimeError                 Traceback (most recent call last)
<ipython-input-9-7de64aad634f> in <module>
      2     print(10/0)
      3 except Exception as e:
----> 4     raise RuntimeError("something is wrong") from e
      5 

RuntimeError: something is wrong

如果一个异常在 except 子句或 finally 子句中被抛出,类似的机制会隐式地发挥作用,之前的异常将被关联到新异常的 __context__ 属性。例如:

In [10]: try: 
    ...:     print(10/0) 
    ...: except: 
    ...:     raise RuntimeError("something is wrong") 
    ...:
-----------------------------------------------
ZeroDivisionError           Traceback (most recent call last)
<ipython-input-10-e950a6292482> in <module>
      1 try:
----> 2     print(10/0)
      3 except:

ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

RuntimeError                Traceback (most recent call last)
<ipython-input-10-e950a6292482> in <module>
      2     print(10/0)
      3 except:
----> 4     raise RuntimeError("something is wrong")
      5 

RuntimeError: something is wrong

异常串连可通过在 from 子句中用 None 来显示地禁止:

In [11]: try: 
    ...:     print(10/0) 
    ...: except: 
    ...:     raise RuntimeError("something is wrong") from None 
    ...:
-------------------------------
RuntimeError                   Traceback (most recent call last)
<ipython-input-11-1818bd8b9d31> in <module>
      2     print(10/0)
      3 except:
----> 4     raise RuntimeError("something is wrong") from None
      5 

RuntimeError: something is wrong

用户自定义异常

Python允许用户自定义异常类,通常应该直接或间接地继承自 Exception 类。

自定义的异常类的名称通常以“Error”结尾,类似与内置标准异常的命名。自定义的异常类,可以像其它类那样可以执行任何操作,但通常保持简单,只提供用以处理程序为异常提取有关错误信息的属性。为模块自定义多个不同错误的异常时,通常是为该模块定义一个异常基类,再为不同错误创建特定的子类。例如:

class ModuleError(Exception):
    '''模块的异常基类'''
    pass


class ModuleNameError(ModuleError):
    '''模块的特定异常子类'''
    pass


class ModuleValueError(ModuleError):
    '''模块的另一个特定异常子类'''
    pass

最后的清理操作:finally 子句

finally 子句是 try 语句的一个可选子句,用于定义在任何情况下都执行的操作,叫做“清理操作”。例如:

In [12]: try: 
    ...:     raise NameError 
    ...: finally: 
    ...:     print('Bye :)') 
    ...:      
    ...:
Bye :)
-------------------------------
NameError        Traceback (most recent call last)
<ipython-input-12-9cda1523ce81> in <module>
      1 try:
----> 2     raise NameError
      3 finally:
      4     print('Bye :)')
      5 

NameError: 

finally 子句总会在离开 try 语句前被执行,无论发生异常与否。当在 try 子句中发生了异常且尚未被 except 子句处理(或者它发生在 except 或 else 子句中)时,该异常将在 finally 子句执行后被重新抛出。 当 try 语句的任何其他子句通过 break, continue 或 return 语句离开时,finally 也会在“离开之前”被执行,参考下面这个更复杂的例子:

In [13]: def divide(a, b): 
    ...:     try: 
    ...:         result = a / b 
    ...:     except ZeroDivisionError: 
    ...:         print('divided by zero!') 
    ...:     else: 
    ...:         print('result is', result) 
    ...:     finally: 
    ...:         print('leaving try') 
    ...:

In [14]: divide(8, 2)
result is 4.0
leaving try

In [15]: divide(8, 0)
divided by zero!
leaving try

In [16]: divide('a', 2)
leaving try
-----------------------
TypeError              Traceback (most recent call last)
<ipython-input-16-324d9fa22da2> in <module>
----> 1 divide('a', 2)

<ipython-input-13-5e4380c62566> in divide(a, b)
      1 def divide(a, b):
      2     try:
----> 3         result = a / b
      4     except ZeroDivisionError:
      5         print('divided by zero!')

TypeError: unsupported operand type(s) for /: 'str' and 'int'

从上面的例子我们看到, finally 子句总是会被执行。但字符串被除时引发了 TypeError 的异常,这个异常没有被 except 子句处理,就会在 finally 子句执行后被重新抛出。

在编程实践中, finally 子句对释放文件或网络连接等外部资源是非常有用的。

总结

编程中,我们不仅要在恰当的地方处理异常,也要在必要的时候抛出异常,我们抛出异常时可以自定义异常。熟练运用异常可以使我们的程序更加健壮,别忘了必要的时候使用 finally 来释放外部资源。

文章来源互联网,如有侵权,请联系管理员删除。邮箱:417803890@qq.com / QQ:417803890

微配音

Python Free

邮箱:417803890@qq.com
QQ:417803890

皖ICP备19001818号
© 2019 copyright www.pythonf.cn - All rights reserved

微信扫一扫关注公众号:

联系方式

Python Free