6. 异常
现在我们来关注异常。
![]() |
程序 (exceptions_01)
第一个脚本说明了管理异常的必要性。
# -*- coding=utf-8 -*-
# 引发错误
x=4/0
我们故意引发一个错误,以查看解释器生成的信息。屏幕显示结果如下:
第 4 行显示:
- 异常类型:ZeroDivisionError;
- 相关的错误信息:整数除法或模运算除以零。该信息为英文。这可能是我们希望修改的内容。
编程的基本原则是,必须尽一切努力避免像上面这样的“意外”崩溃。即使发生错误,程序也应正常结束,并提供有关所发生错误的信息。
异常处理的语法如下:
try:
actions susceptibles de lancer une exception
except [classe d'exception, ...]:
actions de gestion de l'exception
finally:
actions toujours exécutées qu'il y ait exception ou non
在 try 块中,一旦发生异常,操作执行将立即停止。此时,程序将转而执行 except 子句中的操作。
except子句
except [MyException, ...]:
拦截类型为 MyException 或其派生类的异常。当 try 块中发生异常时,解释器会按其编写顺序检查与该 try 块关联的 except 子句。它会停留在第一个能够处理所发生异常的 except 子句上。 如果未找到任何能处理该异常的except子句,则异常将回传至调用方法。如果该方法包含try/except语句,则异常将在此处被处理;否则,异常将继续沿调用方法链向上回传。最终,异常将到达Python解释器。此时,解释器将终止正在执行的程序,并显示如前例所示的错误信息。 因此,规则是:主程序必须捕获所有可能从被调用方法向上传播的异常。
异常会携带有关已发生错误的信息。可以通过以下语法获取这些信息:
except MyException as informations:
信息是一个元组,其中包含与异常相关的信息。
语法
except MyException, erreur:
将与异常相关的错误消息赋值给变量 erreur。
要抛出异常,请使用以下语法
raise MyException(param1, param2, ...)
,其中 MyException 通常是 Exception 类的派生类。传递给该类构造函数的参数将在异常捕获结构的 except 子句中可用。
以下脚本演示了这些概念。
程序 (exceptions_02)
以下脚本显式处理了错误:
# -*- coding=utf-8 -*-
i=0
# 引发错误并进行处理
x=2
try:
x=4/0
except ZeroDivisionError, erreur:
print ("%s : %s ") % (i, erreur)
# x 的值未发生变化
print "x=%s" % (x)
# 重新开始
i+=1
try:
x=4/0
except Exception, erreur:
# 捕获最通用的异常
print ("%s : %s ") % (i, erreur)
# 可以拦截多种类型的异常
i+=1
try:
x=4/0
except ValueError, erreur:
# 此处不会发生该异常
print ("%s : %s ") % (i, erreur)
except Exception, erreur:
# 捕获最通用的异常
print ("%s : (Exception) %s ") % (i, erreur)
except ZeroDivisionError, erreur:
# 捕获特定类型的异常
print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
# 重新尝试并更改顺序
i+=1
try:
x=4/0
except ValueError, erreur:
# 此处未发生该异常
print ("%s : %s ") % (i, erreur)
except ZeroDivisionError, erreur:
# 捕获特定类型的异常
print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
except Exception, erreur:
# 捕获最通用的异常
print ("%s : (Exception) %s ") % (i, erreur)
# 一个无参数的except子句
i+=1
try:
x=4/0
except:
# 不关注异常的性质或错误消息
print ("%s : il y a eu un probleme ") % (i)
# 另一种类型的异常
i+=1
try:
x=int("x")
except ValueError, erreur:
print ("%s : %s ") % (i, erreur)
# 异常通过元组携带信息,程序可访问该元组
i+=1
try:
x=int("x")
except ValueError as infos:
print ("%s : %s ") % (i, infos)
# 可以抛出异常
i+=1
try:
raise ValueError("param1","param2","param3")
except ValueError as infos:
print ("%s : %s ") % (i, infos)
# 可以创建自定义异常
class MyError(Exception):
pass
# 抛出异常 MyError
i+=1
try:
raise MyError("info1","info2", "info3")
except MyError as infos:
print ("%s : %s ") % (i, infos)
# 抛出异常 MyError
i+=1
try:
raise MyError("mon msg d'erreur")
except MyError, erreur:
print ("%s : %s ") % (i, erreur)
# 可以抛出任何类型的对象
class Objet:
def __init__(self,msg):
self.msg=msg
def __str__(self):
return self.msg
i+=1
try:
raise Objet("pb...")
except Objet as erreur:
print "%s : %s" % (i, erreur)
# finally 子句始终会被执行
# 无论是否发生异常
i+=1
x=None
try:
x=1
except:
print "%s : exception" % (i)
finally:
print "%s : finally x=%s" % (i,x)
i+=1
x=None
try:
x=2/0
except:
print "%s : exception" % (i)
finally:
print "%s : finally x=%s" % (i,x)
注:
- 第 6-9 行:处理除以零的情况;
- 第 8 行:捕获了发生的具体异常;
- 第 11 行:由于发生了异常,x 未接收任何值,因此其值未发生变化;
- 第15-19行:重复上述操作,但拦截更高层级的Exception类型异常。 由于异常 ZeroDivisionError 继承自类 Exception,因此 except 子句将捕获它;
- 第 23-33 行:设置多个 except 子句以处理多种异常类型。如果异常不匹配任何 except 子句,则仅执行一个 except 子句或不执行任何子句;
- 第 51-55 行:except 子句可以不带任何参数。在这种情况下,它将终止所有异常;
- 第 59-62 行:引入异常 ValueError;
- 第 66-69 行:获取异常携带的信息;
- 第 73-76 行:介绍了如何抛出异常;
- 第 79-84 行:演示了专有异常类 MyError 的使用;
- 第 79-80 行:类 MyError 仅继承自基类 Exception。它并未向基类添加任何内容。但现在,它可以在 except 子句中被显式指定;
- 第 97-107 行:说明在 Python 中实际上可以抛出任何类型的对象,而不仅仅是继承自 Exception 类的对象;
- 第 109-127 行:演示了 finally 子类的用法。
屏幕输出结果如下:
0 : integer division or modulo by zero
x=2
1 : integer division or modulo by zero
2 : (Exception) integer division or modulo by zero
3 : (ZeroDivisionError) integer division or modulo by zero
4 : il y a eu un probleme
5 : invalid literal for int() with base 10: 'x'
6 : invalid literal for int() with base 10: 'x'
7 : ('param1', 'param2', 'param3')
8 : ('info1', 'info2', 'info3')
9 : mon msg d'erreur
10 : pb...
11 : finally x=1
12 : exception
12 : finally x=None
程序 (exceptions_03)
此新脚本演示了异常在调用方法链中的向上传播:
# -*- coding=utf-8 -*-
# 一个自定义异常
class MyError(Exception):
pass
# 三个方法
def f1(x):
# 不处理异常——它们会自动向上抛出
return f2(x)
def f2(y):
# 不处理异常——它们会自动上报
return f3(y)
def f3(z):
if (z % 2) == 0:
# 如果 z 是偶数,则抛出异常
raise MyError("exception dans f3")
else:
return 2*z
#---------- main
# 异常会沿调用方法链向上传递
# 直到某个方法将其拦截。在此处,该方法将是 main
try:
print f1(4)
except MyError as infos:
print "type :%s, arguments : %s" % (type(infos),infos)
# 方法可以对向上抛出的异常进行扩展
def f4(x):
try:
return f5(x)
except MyError as infos:
# 对异常进行增强处理后重新抛出
raise MyError(infos,"exception dans f4")
def f5(y):
try:
return f6(y)
except MyError as infos:
# 对异常进行增强处理后重新抛出
raise MyError(infos, "exception dans f5")
def f6(z):
if (z % 2) == 0:
# 抛出一个异常
raise MyError("exception dans f6")
else:
return 2*z
#---------- main
try:
print f4(4)
except MyError as infos:
print "type :%s, arguments : %s" % (type(infos),infos)
注:
- 第27-30行,在main的调用中 --> f1 --> f2 --> f3(第28行),由f3抛出的异常MyError将向上传播至main。 该异常随后将由第29行的except子程序处理;
- 第55-58行:在main --> f4 --> f5 --> f6(第56行)的调用中,由f6抛出的异常MyError将向上传播至main。 随后,该异常将由第29行的except子程序处理。此次,在沿调用方法链向上传播的过程中,MyError异常会随着每个向上传播的方法而不断补充信息。
屏幕输出结果如下:
