异常的处理让python更健壮

BG12

Posted by Blue Geek on September 21, 2019

异常的处理让python更健壮

By 青衣极客 Blue Geek In 2019-09-21

在使用python程序的过程中会不可避免地遇到程序处理出错的状况,比如:除数为零,调用不存在的函数,传递传递错误类型的参数等等。 在这种情况下,可能会有两个常见需求:1. 给出明确的错误信息,以便于问题排查和修复; 2. 即使出错,后面的程序还需要继续执行。 那么就需要一套异常的捕获和处理机制,而python提供了一种完善并且简单易用的异常操作接口。

1. 什么是异常?

首先我们来了解一下什么是python下的异常,异常到底长什么样儿?

a = 1 / 0
---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-2-6a8f83430dbe> in <module>
----> 1 a = 1 / 0


ZeroDivisionError: division by zero

比如这个表达式“1 / 0”,只要学过数学的应该都是知道这种表达式无法计算,因此在计算机中将中情况称为“除零异常”。从这个例子可以总结出:异常就是由于某种错误而导致程序无法正常执行的情况。

2. 异常是如何抛出的?

那么异常到底是如何被抛出来的呢?我们是否需要关系抛异常的过程呢?首先回答第二个问题:我们不仅需要关心抛异常的过程,有时我们还需要自己跑异常。这就跟做人一样,遇到无法解决的问题千万不要一个人死扛,抛出去让上层解决就行。这里先演示一下一个简单的抛异常的例子。

if False:
    raise ValueError('异常1')
if True:
    raise ValueError('异常2')
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-5-d8b50705c935> in <module>
      2     raise ValueError('异常1')
      3 if True:
----> 4     raise ValueError('异常2')


ValueError: 异常2

异常是一种运行时错误,只有执行到才会报错。python是一种动态语言,因此除了格式等少数问题会导致程序无法启动之外,遇到的到多数错误都是运行时的错误,也就是异常。python内建以下几种异常: Exception, AttributeError, OSError, IndexError, eyError, NameError, SyntaxError, TypeError, ValueError, ZeroDivisionError。 其中Exception是其他异常类的超类。

那如果我们想抛出的异常不在这些类型中该怎么办?解决这个问题就需要自定义异常类型了。

class MyExcept(Exception):
    pass

raise MyExcept('自定义的异常')
---------------------------------------------------------------------------

MyExcept                                  Traceback (most recent call last)

<ipython-input-4-30ffed614c22> in <module>
      2     pass
      3 
----> 4 raise MyExcept('自定义的异常')


MyExcept: 自定义的异常

自定义的异常类型只需要继承Exception即可。然后在自己需要上报异常的地方抛出。

3. 怎样捕获异常?

有可能大家没有抛异常的需求,但是捕获异常的需求确实真真切切。下面就演示以下“除零异常”的捕获

a = 10
try: 
    a = 1 / 0
    raise TypeError()
except ZeroDivisionError as e:  # 捕获可能出现的具体的异常
    print('除零异常', e)
except  Exception as e:         # 捕获可能出现的所有未知异常
    print('所有异常', e)
else:
    print('没有异常,万事大吉')    # 在没有异常时执行的代码
finally:
    a = 0                       # 收尾操作
print('a =', a)
除零异常 division by zero
a = 0

其实跟捕获异常相关的也就是四个关键字:try、except、else、finally。可能出现异常的代码放在try的子句中,对异常的处理放在except的子句中,没有发生异常的处理放在else子句中,无论是否发生异常都需要执行的收尾工作放在finally子句中。其中try和except是一定需要的,至于else和finally可以根据具体情况决定是否添加。不得不提醒一下的是,异常处理是需要付出一点性能上的代价的。因此可千万别什么程序都用try…except包围起来,还是要区分有威胁的代码和正常的代码。

4. 怎样给出警告但是不终止?

实际开发中有时会遇到一种纠结的情况,那就是确实程序除了问题,但是这个问题还没达到错误的级别,那么还要不要抛异常呢?当然是不能抛异常了,因为抛异常就意味着程序有停机的风险。而对于一些并不严重的问题抛出这种风险是不明智的。不过程序既然出问题了就需要让人知道,以便于评估和修复。python提供了warnings模块可以解决这种报错不停机的需求。

warnings.warn('警告')    # 发出警告
print('hello warning')  # 警告之后程序没有终止
hello warning


/usr/local/lib/python3.7/site-packages/ipykernel_launcher.py:1: UserWarning: 警告
  """Entry point for launching an IPython kernel.

异常其实并不复杂,跟许多其他的语法相比,甚至还十分简单。不过很多使用python的朋友却并不注意对异常的处理,所以常常出现程序崩溃的情况。通过本文的讨论和演示,希望广大的python程序员能够对异常重视起来,毕竟谁也不愿意看见自己的程序莫名其妙地崩溃。

【青衣极客】公众号



COMMENT

博客评论区功能由Github Issue提供,提交Issue时请以本文标题为话题

"BG12-异常的处理让python更健壮"