人归山郭暗,
雁下芦洲白。
——韦应物《夕次盱眙县》
本期文章7100字
根据之前文章的后台统计数据推算
本期预计所需阅读时间40分钟
异常情况(错误)
在之前的代码中,我们已经出过很多次错误了(下文按官方说法称作“异常”,就是程序运行中的错误)。由于代码编写的错误或使用者输入内容的错误,异常会在程序运行出现问题时发生。我们已经多次看到,发生异常时,程序立即停止。
以下例子通过尝试将7除以0来产生ZeroDivisionError异常。
num1 = 7
num2 = 0
print(num1/num2)
运行结果:
>>>
ZeroDivisionError: division by zero
>>>
不同的异常来自不同的原因。下面是一些常见的异常:
ImportError:导入(模块等)失败
IndexError:尝试使用超出实际数组范围的数组元素
NameError:尝试调用未知变量
SyntaxError:代码解析失败,一般是有语法错误
TypeError:数据类型相关错误
ValueError:函数调用时值类型正确了,但值不合适
Python还有其他几种内置的异常,例如ZeroDivisionError和OSError。第三方模块也经常定义自己的异常情况。
测试题8.1.
下面这行代码会造成哪种异常?
print("7" + 4)
点击下方空白区域查看答案
▼
TypeError
因为字符串无法直接与整数相加,所以会产生数据类型错误。
异常情况(错误)的处理
如果我们要处理异常,并在发生异常时自动使用另一份代码,可以使用try / except语句。
这个语句中包含两部分。try部分包含可能引发异常的代码,并需要在except部分写明预计可能会发生的异常类型。如果确实发生了except部分指出的异常(官方称为“捕获”了这种类型的异常,下文中“接管”与“捕获”意思大体相同,因为我习惯叫这个东西是“接管”…感觉这个叫法更直接明了),则try部分的代码将停止运行,并且之后将运行except部分中的代码。如果try部分运行过程中没有错误发生,则except部分的代码不会运行。看例子:
try:
num1 = 7
num2 = 0
print (num1 / num2)
print("Done calculation")
except ZeroDivisionError:
print("An error occurred")
print("due to zero division")
运行结果:
>>>
An error occurred
due to zero division
>>>
在上面的代码中,写出的这个except语句定义了要接管的异常类型(在我们这个例子中,被接管的异常是ZeroDivisionError)。
测试题8.2.
代码的输出结果是?
try:
variable = 10
print (10 / 2)
except ZeroDivisionError:
print("Error")
print("Finished")
点击下方空白区域查看答案
▼
5.0 Finished
由于并没有发生except中指出的ZeroDivisionError异常,所以并不会输出Error。另外,print("Finished")这条语句是在try / except语句之外的。
我们很容易想到,同一个try语句在不同的运行过程中(比如使用者输入的不同内容,或者在不同的系统环境下运行)可能会出现多种错误,所以是否可以有多个不同的except来接管不同异常下的后续运行过程呢?答案是肯定的。
除了在同一个try后面写多个except,我们还可以使用括号将多个异常类型放入同一个except中,以使这个except处理所有括号中列出的这些异常。来看例子:
try:
variable= 10
print(variable+ "hello")
print(variable/ 2)
exceptZeroDivisionError:
print("Divided by zero")
except(ValueError, TypeError):
print("Error occurred")
运行结果:
>>>
Error occurred
>>>
(实际上,上面的例子发生的是ValueError,因为我们在print(variable + "hello")这条语句中把整数与字符串相加了。)
测试题8.3.
代码的输出结果是?
try:
meaning = 42
print(meaning / 0)
print("the meaning of life")
except (ValueError, TypeError):
print("ValueError or TypeError occurred")
except ZeroDivisionError:
print("Divided by zero")
点击下方空白区域查看答案
▼
Divided by zero
要注意的是,上文说过,如果确实发生了except部分指出的某种异常,则try部分的代码将立即停止运行,所以上面例子中the meaning of life这条字符串不会被输出,因为它在发生异常的代码的后面。
如果我们在except语句中没有指定任何特定类型的异常,那么这种except语句将接管所有类型的异常。建议谨慎使用这种except语句,因为它可能会接管意料之外的错误,使我们难以发现代码中没有意识到的错误。
例如:
try:
word = "spam'
print(word / 0)
except:
print("An error occurred")
运行结果:
>>>
An error occurred
>>>
可以看到,这种except语句接管了所有类型的异常。如果我们真的像例子里这样写了,我们的目的很可能只是想让except语句接管word / 0那里的异常的,但是仔细一看我们可以发现,在把"spam"赋值给word变量时,我们使用的引号是不成对的,也就是说这里有语法错误,是SyntaxError。由于这个异常比后面的word / 0处的异常更先发生,所以实际上except语句接管的是前面这个SyntaxError异常。由此可以看出,有的时候在测试时运行出现异常可以帮助我们发现代码中意外的错误,但是不指定异常类型的except语句在某种意义上说会屏蔽掉这些错误,所以一般不建议这样使用except语句。
finally语句
如果无论发生什么错误,都要确保某些代码可以被运行,这时候我们可以使用finally语句。
finally语句按Python语法规定,被放在try / except语句的底部。如果try部分没有出现错误,即except部分没有接管运行过程的话,finally语句中的代码在try中的代码运行后运行;如果try部分出现了错误,except接管了运行过程,那么finally语句中的代码就在相应的except的代码运行后运行。总之,finally语句中的代码肯定会被运行,而且是在最后被运行。来看例子:
try:
print("Hello")
print(1 / 0)
except ZeroDivisionError:
print("Divided by zero")
finally:
print("This code will run no matter what")
运行结果:
>>>
Hello
Divided by zero
This code will run no matter what
>>>
上面的例子是try部分出现了异常,except接管了运行过程的。换个例子:
try:
print("Hello")
print(1)
except ZeroDivisionError:
print("Divided by zero")
finally:
print("This code will run no matter what")
运行结果:
>>>
Hello
1
This code will run no matter what
>>>
上面这个例子里try部分没有产生任何错误,except部分没有接管运行过程。
测试题8.4.
代码的输出结果是?
try:
print(1)
except:
print(2)
finally:
print(3)
领取专属 10元无门槛券
私享最新 技术干货