6. 异常
接下来我们将探讨异常。
程序 (exceptions_01)
第一个脚本说明了处理异常的必要性。
| # -*- coding=utf-8 -*-
# we cause an error
x=4/0
|
我们故意引发错误,以查看解释器生成的信息。输出结果如下:
| Traceback (most recent call last):
File "D:\data\istia-1112\python\tutoriel\exceptions_01.py", line 4, in <module>
x=4/0
ZeroDivisionError: integer division or modulo by zero
|
第 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 [MyException, ...]:
会拦截类型为 MyException 或其派生类的异常。当 try 块中发生异常时,解释器会按编写顺序检查与该 try 块关联的 except 子句。它会在第一个能够处理所发生异常的 except 子句处停止。如果找不到,异常将回传至调用方法。 如果该方法包含 try/except 代码块,则再次处理该异常;否则,异常将继续向上沿调用方法链传播。最终,异常将到达 Python 解释器。此时解释器会终止正在运行的程序,并显示如前例所示类型的错误信息。因此,规则是主程序必须捕获所有可能从被调用方法传播过来的异常。
异常携带有关已发生错误的信息。可以使用以下语法检索这些信息:
except MyException as informations:
information 是一个元组,其中包含与该异常相关的信息。
语法
except MyException, erreur:
将与该异常相关的错误消息赋值给变量 error。
要抛出异常,请使用以下语法
raise MyException(param1, param2, ...)
其中 MyException 通常是继承自 Exception 类的子类。传递给该类构造函数的参数将在异常处理结构的 except 子句中可用。
以下脚本演示了这些概念。
程序 (exceptions_02)
以下脚本显式处理了错误:
| # -*- coding=utf-8 -*-
i=0
# cause an error and manage it
x=2
try:
x=4/0
except ZeroDivisionError, erreur:
print ("%s : %s ") % (i, erreur)
# the value of x has not changed
print "x=%s" % (x)
# here we go again
i+=1
try:
x=4/0
except Exception, erreur:
# we intercept the most general exception
print ("%s : %s ") % (i, erreur)
# several types of exceptions can be intercepted
i+=1
try:
x=4/0
except ValueError, erreur:
# this exception does not occur here
print ("%s : %s ") % (i, erreur)
except Exception, erreur:
# we intercept the most general exception
print ("%s : (Exception) %s ") % (i, erreur)
except ZeroDivisionError, erreur:
# we intercept a specific type
print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
# start again, changing the order
i+=1
try:
x=4/0
except ValueError, erreur:
# this exception does not occur here
print ("%s : %s ") % (i, erreur)
except ZeroDivisionError, erreur:
# we intercept a specific type
print ("%s : (ZeroDivisionError) %s ") % (i, erreur)
except Exception, erreur:
# we intercept the most general exception
print ("%s : (Exception) %s ") % (i, erreur)
# an except clause with no arguments
i+=1
try:
x=4/0
except:
# we're not interested in the nature of the exception or the error msg
print ("%s : il y a eu un probleme ") % (i)
# another type of exception
i+=1
try:
x=int("x")
except ValueError, erreur:
print ("%s : %s ") % (i, erreur)
# an exception transports information in a tuple accessible to the program
i+=1
try:
x=int("x")
except ValueError as infos:
print ("%s : %s ") % (i, infos)
# exceptions can be thrown
i+=1
try:
raise ValueError("param1","param2","param3")
except ValueError as infos:
print ("%s : %s ") % (i, infos)
# you can create your own exceptions
class MyError(Exception):
pass
# throw MyError exception
i+=1
try:
raise MyError("info1","info2", "info3")
except MyError as infos:
print ("%s : %s ") % (i, infos)
# throw MyError exception
i+=1
try:
raise MyError("mon msg d'erreur")
except MyError, erreur:
print ("%s : %s ") % (i, erreur)
# any type of object can be launched
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)
# the finally clause is always executed
# whether or not there is an exception
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 -*-
# a proprietary exception
class MyError(Exception):
pass
# three methods
def f1(x):
# exceptions are not handled - they are automatically logged
return f2(x)
def f2(y):
# exceptions are not handled - they are automatically logged
return f3(y)
def f3(z):
if (z % 2) == 0:
# if z is even, throw an exception
raise MyError("exception dans f3")
else:
return 2*z
# ---------- hand
# exceptions go up the chain of methods called
# until a method intercepts it. In this case, it will be main
try:
print f1(4)
except MyError as infos:
print "type :%s, arguments : %s" % (type(infos),infos)
# a method can enrich the exceptions it raises
def f4(x):
try:
return f5(x)
except MyError as infos:
# we enrich the exception then relaunch it
raise MyError(infos,"exception dans f4")
def f5(y):
try:
return f6(y)
except MyError as infos:
# we enrich the exception then relaunch it
raise MyError(infos, "exception dans f5")
def f6(z):
if (z % 2) == 0:
# throw an exception
raise MyError("exception dans f6")
else:
return 2*z
# ---------- hand
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 异常会被沿途每个方法添加的信息所丰富。
屏幕输出如下:
| type :<class '__main__.MyError'>, arguments : exception dans f3
type :<class '__main__.MyError'>, arguments : (MyError(MyError('exception dans f6',), 'exception dans f5'), 'exception dans f4')
|