优雅地处理 Python 中的异常
最近学习 Scala
的异常处理模块时 Scala 模式匹配与异常处理,发现自己对于 Python
中的异常处理地非常不好并且进行 code review
时代码体验非常差,因此本文简单介绍下 Python 中的异常处理以及如何扩展其异常处理模块!
背景
为什么需要进行异常处理呢?例如解析器去执行程序并检测到了一个错误时,触发异常,异常触发后且没被处理的情况下,程序就在当前异常处终止,后面的代码不会运行,那么就这样崩溃的软件必然带给用户糟糕的体验。所以必须提供一种异常处理机制来增强你程序的健壮性与容错性。
python 详细的异常处理情况可以参考教程:https://www.runoob.com/python/python-exceptions.html
首先引入python中的常用操作作为示例来了解 python 中的异常,例如除法运算和文件读取操作,如下所示:
1 | def example(num1, num2, path): |
上述代码定义的函数 example
中包含三个参数:num1
,num2
,path
,函数内首先执行除法运算再执行文件读操作!如果没有发生异常时上述代码可以正确执行,但正常情况下都需要对操作可以发生的异常进行判断并对抛出的异常进行处理,这样才能增加代码的健壮性!
如果不添加异常处理模块,以上述函数 example
为例直接添加异常测试,那么可能出现以下异常情况:
1 | # 除数为0那么会抛出 ZeroDivisionError 异常 |
---------------------------------------------------------------------------
ZeroDivisionError Traceback (most recent call last)
<ipython-input-2-fcf03ade5770> in <module>
1 # 除数为0那么会抛出 ZeroDivisionError 异常
----> 2 example(1, 0, 'test.txt')
<ipython-input-1-7f29291a2290> in example(num1, num2, path)
1 def example(num1, num2, path):
----> 2 result = num1 / num2
3 with open(path, 'r') as file:
4 file.read()
ZeroDivisionError: division by zero
1 | # 除法运算操作数的类型不为数字时那么会抛出 TypeError 异常 |
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-3-12485c8f4513> in <module>
1 # 除法运算操作数的类型不为数字时那么会抛出 TypeError 异常
----> 2 example(1, 'a', 'test.txt')
<ipython-input-1-7f29291a2290> in example(num1, num2, path)
1 def example(num1, num2, path):
----> 2 result = num1 / num2
3 with open(path, 'r') as file:
4 file.read()
TypeError: unsupported operand type(s) for /: 'int' and 'str'
1 | # 读文件时如果当前路径下不存在该文件则会抛出 FileNotFoundError 异常 |
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
<ipython-input-4-84ce3176b749> in <module>
1 # 读文件时如果当前路径下不存在该文件则会抛出 FileNotFoundError 异常
----> 2 example(1, 2, 'test.txt')
<ipython-input-1-7f29291a2290> in example(num1, num2, path)
1 def example(num1, num2, path):
2 result = num1 / num2
----> 3 with open(path, 'r') as file:
4 file.read()
FileNotFoundError: [Errno 2] No such file or directory: 'test.txt'
如果不进行异常处理执行上述定义的 example
函数就可能抛出这么多异常并且在终端输出这么多错误信息,作为一个处女座的程序员简直是难以忍受啊!
因此,我们在函数中的运算操作中加上异常处理判断:
1 | def example(num1, num2, path): |
在函数中添加异常处理语句之后,再次测试异常情况:
1 | example(1, 0, 'test.txt') |
num2 cannot be zero!
num1 and num2 should be number!
file test.txt not found!
终端输出这些异常信息就看起来舒服多了啊,而且异常抛出时后续的语句同样执行!
但是,上述异常处理语句同样存在问题:
- 代码语句复杂,异常处理语句比操作运算都多!
- 如果代码中包含相同的异常处理情况而
try...except
语句不可复用! - 逻辑代码模块与异常处理模块在同一区域略显复杂,如果在单独一个模块中专门用来异常处理?
针对上述两个问题,可以使用 python 第三方库 merry
来解决!
Merry 异常处理库
Merry 这第三方异常处理库的目的是将异常处理与业务逻辑分离,通过装饰器来实现异常检查和异常处理!
Merry 安装:pip install merry
在安装merry库之后,上述定义的 example
函数中异常处理可以重写为以下形式:
1 | from merry import Merry |
上述代码中通过 Merry 库的_try
和 _except
装饰器实现了异常的监听和处理,对于每一种异常情况都有其对应的处理方法,这样代码格式就好看多了啊!而且将异常处理方法和逻辑代码分离开不就可以重用了!
测试下异常处理的效果:
1 | example(1, 0, 'test.txt') |
zero_division_error division by zero
type_error unsupported operand type(s) for /: 'int' and 'str'
file_not_found_error [Errno 2] No such file or directory: 'test.txt'
这样不就舒服多了啊,而且还可以对异常函数进行封装!
当然本文仅介绍了简单的用法 更多用法可以参考源码链接哦!传送门~