![自学Python:编程基础、科学计算及数据分析](https://wfqqreader-1252317822.image.myqcloud.com/cover/254/34659254/b_34659254.jpg)
2.5 异常与警告
异常和警告是编写程序过程中经常遇到的问题。为了更好地处理编程中遇到的问题,我们需要了解异常和警告的相关知识。
2.5.1 异常
1. 捕捉异常
我们在运行代码时经常遇到程序出错的情况,在Python中,这些错误通常叫做异常(Exception)。
例如,有这样一个用于计算以10为底的对数的程序,该程序使用raw_input()函数从命令行读取输入,计算它的对数并输出结果,直到我们输入为q为止:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/88_03.jpg?sign=1738868007-5VrDwTFw255SXNJJwJd1ZvlD6mJEbnTw-0-af6441545077bfaa9abd5407b10f8d90)
乍看起来,程序似乎没甚么问题,然而,当我们输入一个负数时,程序抛出了一个ValueError异常,因为对数函数不能接受一个非正值输入:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/88_04.jpg?sign=1738868007-Crv2Qb8CGI4PQRnwxXbbxpBuGjjG53pL-0-12dc998ffa7c51f30666243e6cf03c63)
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/89_01.jpg?sign=1738868007-FeKFLJyEwLMs9DGMd1R1ap4mhRh8twri-0-7599ad0fd164126458258752ac347da1)
正常情况下,Python程序在抛出异常后就会停止执行。如果不希望程序停止运行,可以使用一对关键字try和except来处理异常,其基本形式如下:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/89_02.jpg?sign=1738868007-Q9H4MuRAvmLST2imwIPB0NzbCFh9FvJB-0-7093836b8513c0dd02d7dee941ef2587)
我们将可能抛出异常的代码,放入try块中,然后使用except块处理相应的异常。
当try块中的代码遇到异常时,这个异常会首先被传到except块。如果except块能处理这个异常,则执行except块相应的内容,然后程序继续执行;如果不能,该异常将被继续传递。
在上面的例子中,代码抛出的异常类型是ValueError,因此,我们改写程序,将可能出错的部分放入try块,并用关键字except处理ValueError类型的异常:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/89_03.jpg?sign=1738868007-wPl6Sk7Dz58s98awNBauMPEjJDTB4Vua-0-621741ecc248491f7c500e69a2d8ddc7)
再执行这个程序时,输入负数或者0都不会中断程序,而是打印expect块输出的信息:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/89_04.jpg?sign=1738868007-Sy0krSFjvHSuPGDOaO5fVdKPYOg0mbF1-0-e3cb2a0d7d89248a6ba2ef9398a84439)
2. 处理不同类型的错误信息
我们对上面的代码进行修改,将y的值改为1/math.log10(x):
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/89_05.jpg?sign=1738868007-qlRQ6kesSwWB2UfNdYDgxMbfRQX5RX5q-0-f6c8f138fa7293bbf14ffa24c433c63c)
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/90_01.jpg?sign=1738868007-PG0oUeSIHLx0S5UxQMwiMD1clNgjUh4U-0-57bdaf1f277fb0026e3e50d37ee2ec22)
如果输入1:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/90_02.jpg?sign=1738868007-Sv9l7Vj3Xfj1dy2sowbZ5MJtZGjrCPg3-0-606893046eb9283947611d37c7d160e3)
因为1的对数为0,而1/0是一个非法操作,所以Python抛出了一个ZeroDivisionError类型的异常。
这个异常首先传到except块中,但我们定义的except块并不能处理这种类型的异常,因此,该异常被继续传递,程序停止运行。
这个问题有以下几种解决方式。
1)第一种方式,可以使用Exception替换ValueError,直接捕获所有的异常。Exception类型是各种异常的总称,所以ZeroDivisionError类型和ValueError类型都是一种特殊的Exception:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/90_03.jpg?sign=1738868007-Oqvd7mVBzOL0WPmEYYtFQO9oCVYLqMAd-0-664d207a7d6ceff7a486e1ac3302f34c)
因此,下面的异常都会被except块处理:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/91_01.jpg?sign=1738868007-ugSjtkOI2rDGv66f2bZ8QzzWGyHs1xv7-0-6743f95c9c86ceb00bcd3bc113efeac0)
2)第二种方式,我们可以在一个except块中声明多个异常类型:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/91_02.jpg?sign=1738868007-VqMEhJPKqm3JB326exWxub68D07S3yL8-0-374e32c7fe82a002b47f36d864ac5d1a)
程序运行抛出其中任意一种类型的异常都会被except块所处理。
3)第三种方式,通过多个except块分别处理各种类型的异常,每个except块负责处理一种类型的异常:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/91_03.jpg?sign=1738868007-TogohBBqyPKm38Lf7PjTl081GQfg6UOE-0-83e9fc6e5b2dfc14f554d09fedadafd9)
在这种情况下,两种类型的异常会被程序分别处理:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/91_04.jpg?sign=1738868007-RBDOMX3yi0EV34LOLJpNBBtlA82YEpQK-0-28a3a1cb158fff2209dce7ffa6951599)
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/92_01.jpg?sign=1738868007-wIeajDdH8lF2TMwMz3pAl4YaNNgdFdyM-0-4023a629dddbd39fa7e7502113482d25)
3. 得到异常的具体信息
当我们输入字符串“abcde”时,上面的程序会提示:“the value must be greater than 0”,这与实际情况不符。抛出ValueError异常的部分并不是math.log10()函数,而是float()函数。
调用float('abcde')时,由于所给字符串不能转化为浮点数,Python会抛出一个“ValueError: could not convert string to float: abcde”的异常。这个异常包含两部分的内容:前面的部分表示异常的类型,后面的部分表示异常的具体说明。
在except块中,我们可以这样获得异常的具体信息:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/92_02.jpg?sign=1738868007-frNV0AJjg0jkIX6BUdeoKYMpxRXcKsjf-0-77d3c97de1dd9511a587319eb1e5c92f)
利用这种方式,我们首先将捕获到的异常保存在变量e中,并用属性.message查看相关的说明信息。
为了得到异常的具体信息,修改except块的部分:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/92_03.jpg?sign=1738868007-jmLW4g2x5jIc8OFnFcnIFIPaygPLrsbj-0-a043a4c1967f0291e35c9604e32c3e3f)
运行后,当我们输入非法值时,就能得到异常的具体信息:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/92_04.jpg?sign=1738868007-9zu3RGkTeURHRLTLHjBfXW5MS4orc1wy-0-9bcfc2a39a3dfa117649687193e99c74)
4. 抛出异常
在程序运行过程中,我们可以使用关键字raise抛出异常。
例如,当变量month为不合法的月份时抛出异常:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/93_01.jpg?sign=1738868007-P6z9M4U1UPViGMSLPyCXFhbQhXyOawb8-0-b86f9af3466fa197e341e71d681b36d8)
抛出的异常类型为ValueError,括号中为具体说明信息。
5. finally关键字
异常处理时,我们还可以加入一个以关键字finally开头的代码块,其作用为:不管try块中的代码是否抛出异常,finally块中的内容总是会被执行。
没有异常时,finally块会在try块的代码执行完毕后执行;出现异常时,finally块会在抛出异常前执行。因此,finally块可以用来作为程序抛出异常时的安全保证,比如确保打开的文件被正确关闭。
例如,一个没有异常的finally块:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/93_02.jpg?sign=1738868007-qEkiqunXG3wTugKtq1xto8ci9JwgNYPS-0-55ca70788d1336e560035b1da841594c)
如果异常被except块处理了,finally块在异常被处理后执行:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/93_03.jpg?sign=1738868007-OsGKq1jh2y54UCyYaTRQFU8tfXm0P6eE-0-ce0af5af5ffa6bcc2b19ec27a6188c9b)
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/94_01.jpg?sign=1738868007-50PSztAGBYyXKwdSWNSofZiTnAKrhx9Z-0-fb8f709b70e7fa90cad3cc89fd2979d8)
finally块的执行顺序总结如下:
● 没有异常,try块结束后执行;
● 异常抛出,except块没有处理异常,在抛出异常前执行;
● 异常抛出,except块处理了异常,在异常被处理后执行。
2.5.2 警告
在Python中,警告(Warning)通常用来告知用户某种做法是不好的,但这种做法不会影响程序的正常运行。
使用警告需要预先导入相关的模块:
In [1]: import warnings
然后调用warnings模块中的warn函数来抛出警告:
warn(msg, WarningType = UserWarning)
msg是警告的提示信息,WarningType参数用来指定警告的类型,如果不指定,默认的类型是UserWarning(用户警告):
In [2]: warnings.warn("test")
C:\Miniconda2\Scripts\ipython-script.py:1: UserWarning: test
常见的警告类型主要有:
● Warning,所有警告的父类,所有的警告都能看成一个Warning类;
● UserWarning,用户警告,warn函数的默认类型;
● DeprecationWarning,表示用户使用了未来会被废弃的功能;
● FutureWarning,表示用户使用了未来可能会改变的功能;
● RuntimeWarning,运行时警告。
有时候,我们在运行程序时不希望看见某种类型的警告,可以使用warnings模块中的filterwarnings来进行筛选:
![](https://epubservercos.yuewen.com/9B8D30/18513172808564606/epubprivate/OEBPS/Images/94_02.jpg?sign=1738868007-7DFIiJtJHTE86ei2O9LXTYoj0HgLwy0H-0-91fbcc2b3206851f4372a0029f12c495)
在程序运行时,所有RuntimeWarning类型的警告都不会被显示。