Skip to content

6. 异常

现在我们来关注异常。

  

程序 (exceptions_01)

第一个脚本说明了管理异常的必要性。


# -*- coding=utf-8 -*-

# 引发错误
x=4/0

我们故意引发一个错误,以查看解释器生成的信息。屏幕显示结果如下:

1
2
3
4
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子句


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异常会随着每个向上传播的方法而不断补充信息。

屏幕输出结果如下:

type :<class '__main__.MyError'>, arguments : exception dans f3
type :<class '__main__.MyError'>, arguments : (MyError(MyError('exception dans f6',), 'exception dans f5'), 'exception dans f4')