一、前言
本章主要解决一下前文曾提到的两个东西:模块、异常。
事实上这两个之间并没有什么直接的关联,写在同一章的原因仅仅只是因为只写一个的话,会导致篇幅太短,因为都很简单。
二、异常
我们首先来聊聊异常,这是很多高级语言都拥有的一个特性。
它并非什么高大上的东西,其仅仅只是一种程序错误的处理方式。
较低级的语言不使用它的原因很简单:对程序的性能影响较大,所以一般会更倾向于通过返回值来判断。
但对于像python
这种高级语言,那就无所谓了,异常几乎随处可见,与java
差不多。
为了更好理解异常,首先我们来看看低级语言是如何处理错误的:
def divi(a, b):
if b == 0:
return None
return a / b
ret = divi(10, 0)
if ret == None:
print("函数运行出错")
else:
print(f'10/0={ret}')
就像上面这个函数一样,求两个数相除的结果,而除数b是不能为0的对吧?
所以如果这个函数检测到了b为0,那就可以通过返回None
来代表当前函数出现了错误。
None
在python中代表一个特殊的“空值”。
这种方法虽然效率较高,但对于我们程序员来说并不友好,你只知道这个函数调用出错了,但你却无法通过返回的None
值知道具体错误的原因!
而这时就可以使用异常来替换上面的这种方式,上面的代码改为异常就是下面这样:
def divi(a, b):
if b == 0:
raise ValueError('除数不能为0!')
return a / b
ret = divi(10, 0)
异常也是一个类,称为异常类,这样的类有很多,这里使用的是一个比较简单的ValueError
异常类,可以通过raise
关键字抛出其后生成的这个异常对象,填入的参数就是我们想要告诉给外面调用者的信息!
这时,一旦调用这个函数,你就会发现下面这样的错误信息:
看起来是不是很眼熟?你写代码的过程中估计也常常会遇到这样的报错信息。
没错,这就是异常!只不过别人写的异常信息是用英文写的,我这里写的中文而已。
也就是说,一旦通过raise
关键字抛出异常了,那程序就会立马报错,并停止运行。
但这很多时候并不是我们想要的,可能我们更希望的是:如果程序出现了错误,那我就先尝试去修复它,如果修复也失败了,程序再终止也不迟对吧?
所以这时候我们就要用到try except
语句了:
try:
ret = divi(10, 0)
except:
print("出现错误!")
它的用法同样不难,你只需要将你想要正常执行的代码放入try
后面的语句块中即可。
如果发生了异常,那就会去跳到后面的except
语句块中执行。
注意:如果
try
语句块中没有出现任何异常,那么后面的except
语句块中的代码就不会执行!
这是最简单的用法,并且你会发现,程序现在已经可以正常执行了:
只不过执行的代码为后面的异常块,但至少程序没有直接终止不是吗?
但这样捕获异常太过于宽泛了,虽然我们这里只抛出了一个ValueError
异常类,但实际上python
中的异常类是相当多的。
为了能够修复异常,我们就得捕获特定的异常,也很简单:
try:
ret = divi(10, 0)
except ValueError:
print("出现ValueError异常类错误")
except Exception:
print("出现其它异常类错误!")
你可以在其后面添加特定的异常类即可,如果try
中的语句块出现了对应的异常,那就会去执行该异常块对应的语句。
其中Exception
代表了所有的异常类基类,也就是前面的异常没有匹配到,那就都到这个异常块中来。
这是属于类多态的性质,即:父类对象可以接受子类对象,这里就是父类异常可以接受所有它的子类异常类,但由于python是弱类型语言,多态很难体现出来(因为就算没有多态……一个类型的变量也可以轻易改变到另一个类型)
这样就可以修复代码了:
try:
ret = divi(10, 0)
except ValueError:
ret=divi(10,1) # 修复,重新调用
except Exception:
print("出现其它异常类错误!")
如果你还想要知道具体的错误信息,那么还可以使用as
关键字:
try:
ret = divi(10, 0)
except ValueError as err:
ret = divi(10, 0)
print(err) # 打印抛出的异常类对象
except Exception as err:
print(err) # 打印抛出的异常类对象