Python 中的异常处理是指管理在程序执行过程中可能出现的运行时错误。在 Python 中,当执行期间出现错误或意外情况时,比如除以零、试图访问不存在的文件、或者尝试对不兼容的数据类型执行操作,就会引发异常。
Python 提供了两个非常重要的特性来处理 Python 程序中的任何意外错误,并添加调试能力:
-
异常处理 — 这将在本教程中介绍。以下是 Python 中可用的标准异常列表:标准异常。
-
Python 中的断言
断言是一种 sanity-check(合理性检查),你可以在测试程序时开启,在测试完成后关闭。
最容易理解断言的方式就是把它当作一个 raise-if 语句(或者更准确地说是一个 raise-if-not 语句)。测试一个表达式,如果结果为假,则抛出一个异常。
断言是由 assert
语句执行的,这是 Python 中的最新关键字,从版本 1.5 开始引入。 程序员经常在函数开始处放置断言来检查输入的有效性,并在函数调用后检查输出的有效性。
断言语句
当 Python 遇到一个 assert
语句时,它会评估伴随的表达式,这个表达式最好是真实的。如果表达式为假,Python 会抛出一个 AssertionError
异常。
断言的语法如下:
assert Expression[, Arguments]
如果断言失败,Python 使用 ArgumentExpression
作为 AssertionError
的参数。AssertionError
异常可以像其他异常一样使用 try-except
语句捕获和处理,但如果未处理,它们将终止程序并产生回溯。
示例
下面是一个将温度从开尔文度转换为华氏度的函数。由于零度开尔文是温度的极限,如果遇到负温度,函数会退出。
def KelvinToFahrenheit(Temperature):
assert (Temperature >= 0),"温度低于绝对零度!"
return ((Temperature-273)*1.8)+32
print (KelvinToFahrenheit(273))
print (int(KelvinToFahrenheit(505.78)))
print (KelvinToFahrenheit(-5))
当上述代码被执行时,会产生如下结果:
32.0
451
Traceback (most recent call last):
File "test.py", line 9, in <module>
print (KelvinToFahrenheit(-5))
File "test.py", line 4, in KelvinToFahrenheit
assert (Temperature >= 0),"温度低于绝对零度!"
AssertionError: 温度低于绝对零度!
什么是异常?
异常是一个事件,发生在程序执行期间,会中断程序的正常流程。一般来说,当 Python 脚本遇到其无法处理的情况时,它会引发异常。异常是一个代表错误的 Python 对象。
当 Python 脚本引发异常时,必须立即处理异常,否则它会终止并退出。
在 Python 中处理异常
如果你有一些可疑的代码,可能会引发异常,你可以通过将可疑的代码放在 try:
块中来保护你的程序。在 try:
块之后,包括一个 except:
语句,后面跟着尽可能优雅地处理问题的一段代码。
try:
块包含可能会引发异常的操作。
如果异常发生,程序跳转到 except:
块。
如果没有异常发生在 try:
块中,except:
块会被跳过。
语法
以下是 try...except...else
块的简单语法:
try:
你在这里执行操作
......................
except ExceptionI:
如果有 ExceptionI,则执行这块代码。
except ExceptionII:
如果有 ExceptionII,则执行这块代码。
......................
else:
如果没有异常,则执行这块代码。
以下是关于上述语法的一些重要点:
-
单个
try
语句可以有多个 except
语句。这在 try
块包含可能引发不同类型异常的语句时非常有用。
-
你也可以提供一个通用的
except
子句,它可以处理任何异常。
-
在
except
子句之后,你可以包含一个 else
子句。如果 try:
块中的代码没有引发异常,else
块中的代码将会执行。
-
else
块是一个放置不需要 try:
块保护的代码的好地方。
示例
下面的例子打开一个文件,写入内容,并优雅地退出,因为没有任何问题。
try:
fh = open("testfile", "w")
fh.write("这是一个用于异常处理的测试文件!!")
except IOError:
print ("错误:找不到文件或读取数据")
else:
print ("成功写入文件内容")
fh.close()
它会产生如下输出:
成功写入文件内容
然而,如果将 open()
函数中的模式参数改为 "w" 并且 testfile
文件不存在,程序将在 except
块中遇到 IOError
并打印如下错误信息:
错误:找不到文件或读取数据
示例
下面的例子尝试打开一个你没有写权限的文件,因此它会引发一个异常。
try:
fh = open("testfile", "r")
fh.write("这是一个用于异常处理的测试文件!!")
except IOError:
print ("错误:找不到文件或读取数据")
else:
print ("成功写入文件内容")
这会产生如下结果:
错误:找不到文件或读取数据
没有指定异常的 except
子句
你也可以在没有定义异常的情况下使用 except
语句,如下所示:
try:
你在这里执行操作;
......................
except:
如果有任何异常,则执行这块代码。
......................
else:
如果没有异常,则执行这块代码。
这种形式的 try-except
语句会捕捉所有异常。虽然使用这种形式的 try-except
语句可以捕捉所有异常,但这不是一种好的编程实践,因为它捕捉了所有异常但并没有让程序员识别出问题的根本原因。
多个异常的 except
子句
你也可以使用同一个 except
语句来处理多个异常,如下所示:
try:
你在这里执行操作;
......................
except(Exception1[, Exception2[,...ExceptionN]]]):
如果有给定异常列表中的任何异常,
则执行这块代码。
......................
else:
如果没有异常,则执行这块代码。
try-finally
子句
你可以使用 finally:
块与 try:
块一起。finally
块是一个放置任何必须执行的代码的地方,无论 try
块是否引发了异常。try-finally
语句的语法如下:
try:
你在这里执行操作;
......................
由于任何异常,这可能会被跳过。
finally:
这总是会被执行。
......................
你不能在一个 finally
子句中使用 else
子句。
示例
try:
fh = open("testfile", "w")
fh.write("这是一个用于异常处理的测试文件!!")
finally:
print ("错误:找不到文件或读取数据")
如果你没有权限以写模式打开文件,那么这将产生如下结果:
错误:找不到文件或读取数据
同样的例子可以更清晰地重写如下:
try:
fh = open("testfile", "w")
try:
fh.write("这是一个用于异常处理的测试文件!!")
finally:
print ("准备关闭文件")
fh.close()
except IOError:
print ("错误:找不到文件或读取数据")
当在 try
块中抛出异常时,执行立即传递给 finally
块。在 finally
块中的所有语句被执行完毕后,如果在更高一层的 try-except
语句中存在 except
子句,异常会被再次抛出并处理。
异常的参数
异常可以有一个参数,它是一个提供了关于问题附加信息的值。参数的内容因异常而异。你通过在 except
子句中提供一个变量来捕获异常的参数,如下所示:
try:
你在这里执行操作;
......................
except ExceptionType as Argument:
你可以在这里打印 Argument 的值...
如果你编写的是处理单个异常的代码,你可以在 except
语句中异常名称后跟随一个变量。如果你捕获多个异常,可以在异常元组后跟随一个变量。
这个变量接收异常的值,通常包含导致异常的原因。变量可以接收单个值或多个值的形式为元组。这个元组通常包含错误字符串、错误编号和错误位置。
示例
下面是一个单个异常的例子:
def temp_convert(var):
try:
return int(var)
except ValueError as Argument:
print ("参数不包含数字\n", Argument)
temp_convert("xyz")
这会产生如下结果:
参数不包含数字
invalid literal for int() with base 10: 'xyz'
在 Python 中抛出异常
你可以通过使用 raise
语句以多种方式抛出异常。raise
语句的一般语法如下:
语法
raise [Exception [, args [, traceback]]]
这里的 Exception
是异常的类型(例如,NameError
),而 argument
是异常参数的值。参数是可选的;如果不提供,异常参数默认为 None
。
最后一个参数 traceback
也是可选的(并且在实践中很少使用),如果存在的话,它是用于异常的回溯对象。
示例
异常可以是一个字符串、一个类或一个对象。Python 核心抛出的大多数异常都是类,带有类实例作为参数。定义新的异常相当容易,可以按照以下方式完成:
def functionName(level):
if level < 1:
raise "Invalid level!", level
注意:为了捕获一个异常,“except”子句必须引用相同的异常,无论是类对象还是简单的字符串。例如,要捕获上面的异常,我们必须按如下方式编写 except
子句:
try:
except "Invalid level!":
else:
用户定义的异常
Python 也允许你通过从标准内置异常派生类来创建自己的异常。
下面是一个与 RuntimeError
相关的例子。这里创建了一个从 RuntimeError
派生的类。当你需要在捕获异常时显示更多特定的信息时,这是有用的。
在 try
块中,用户定义的异常被抛出并在 except
块中被捕获。变量 e
用于创建该类的一个实例。
class Networkerror(RuntimeError):
def __init__(self, arg):
self.args = arg
一旦你定义了上述类,你可以按照以下方式抛出异常:
try:
raise Networkerror("Bad hostname")
except Networkerror as e:
print(e.args)
标准异常
以下是在 Python 中可用的标准异常列表:
序号 |
异常名称 |
描述 |
1 |
Exception |
所有异常的基础类 |
2 |
StopIteration |
当迭代器的 next() 方法没有指向任何对象时抛出 |
3 |
SystemExit |
由 sys.exit() 函数抛出 |
4 |
StandardError |
除了 StopIteration 和 SystemExit 之外的所有内置异常的基础类 |
5 |
ArithmeticError |
数值计算中发生的所有错误的基础类 |
6 |
OverflowError |
当数值类型超出最大限制时抛出 |
7 |
FloatingPointError |
当浮点数计算失败时抛出 |
8 |
ZeroDivisionError |
当所有数值类型进行除零或取模运算时抛出 |
9 |
AssertionError |
当断言语句失败时抛出 |
10 |
AttributeError |
当属性引用或赋值失败时抛出 |
11 |
EOFError |
当没有从 raw_input() 或 input() 函数中获取输入且到达文件末尾时抛出 |
12 |
ImportError |
当 import 语句失败时抛出 |
13 |
KeyboardInterrupt |
当用户中断程序执行,通常是按下 Ctrl+c 时抛出 |
14 |
LookupError |
所有查找错误的基础类 |
15 |
IndexError |
当序列中找不到索引时抛出 |
16 |
KeyError |
当指定的键不在字典中时抛出 |
17 |
NameError |
当在本地或全局命名空间中找不到标识符时抛出 |
18 |
UnboundLocalError |
当尝试访问函数或方法中的局部变量但没有赋值时抛出 |
19 |
EnvironmentError |
发生在 Python 环境外的所有异常的基础类 |
20 |
IOError |
当输入/输出操作失败时抛出,例如 print 语句或尝试打开不存在的文件时的 open() 函数 |
21 |
IOError |
当发生与操作系统相关的错误时抛出 |
22 |
SyntaxError |
当 Python 语法中有错误时抛出 |
23 |
IndentationError |
当缩进没有正确指定时抛出 |
24 |
SystemError |
当解释器发现内部问题时抛出,但当遇到此错误时 Python 解释器不会退出 |
25 |
SystemExit |
当使用 sys.exit() 函数退出 Python 解释器时抛出。如果代码中未处理,会导致解释器退出 |
26 |
TypeError |
当尝试对指定数据类型执行无效的操作或函数时抛出 |
27 |
ValueError |
当内置函数为数据类型有有效的类型参数,但参数有无效的值时抛出 |
28 |
RuntimeError |
当产生的错误不属于任何类别时抛出 |
29 |
NotImplementedError |
当抽象方法需要在继承类中实现但实际上未实现时抛出 |