处理异常

异常处理通过 try 语句(详细语法及使用规则见 try 语句)来实现。

通过 try 语句处理程序已经出现或可能出现异常,使代码能够继续执行,否则异常未被处理,程序终止执行。

例如下列代码要求用户输入有效的整数:

while True:
    try:
        x = int(input("Please enter a number: "))
        break
    except ValueError:
        print("Oops! That was no valid number. Try again...")
Please enter a number:  3.14


Oops! That was no valid number. Try again...


Please enter a number:  314

try 语句的工作原理如下:

  • 首先执行 try 子句;

  • 如果没有异常发生,则跳过 except 子句并完成 try 语句的执行;

  • 如果在执行 try 子句时发生了异常,则跳过该子句中剩下的部分。然后,如果异常的类型和 except 关键字后面的异常匹配,则执行 except 子句,然后继续执行 try 语句之后的代码;

  • 如果发生的异常和 except 子句中指定的异常不匹配,则将其传递到外部的 try 语句中;如果没有找到处理程序,则它是一个未处理异常,执行将停止并显示异常信息。

一个 try 语句可能有多个 except 子句,以指定不同异常的处理程序,但最多会执行一个处理程序。

处理程序只处理相应的 try 子句中发生的异常。如果发生的异常和 except 子句中的类是同一个类或者是它的基类,则异常和 except 子句中的类是兼容的。如果首先处理了基类,子类不再被处理(子类也被基类处理了):

class B(Exception):
    pass

class C(B):
    pass

class D(C):
    pass

for cls in [B, C, D]:
    try:
        raise cls()
    except D:
        print("D")
    except C:
        print("C")
    except B:
        print("B")
B
C
D
class B(Exception):
    pass

class C(B):
    pass

class D(C):
    pass

for cls in [B, C, D]:
    try:
        raise cls()
    except B: # 下列都是 B 的子类,不再被处理
        print("B")
    except C:
        print("C")
    except D:
        print("D")
B
B
B

最后的 except 子句可以省略异常名,以用作通配符。但请谨慎使用,因为以这种方式很容易掩盖真正的编程错误!它还可用于打印错误消息,然后重新引发异常:

import sys

try:
    3/0
except ValueError as v:
    print(v)
except:
    print("Unexpected error:", sys.exc_info()[0])
    raise
Unexpected error: <class 'ZeroDivisionError'>


---------------------------------------------------------------------------

ZeroDivisionError                         Traceback (most recent call last)

<ipython-input-13-f0b3e840c5ab> in <module>
      2 
      3 try:
----> 4     3/0
      5 except ValueError as v:
      6     print(v)


ZeroDivisionError: division by zero

可选的 else 子句,在使用时必须放在所有的 except 子句后面。对于在 try 子句不引发异常时必须执行的代码来说很有用:

try:
    f = open('../11_built-in_function/test.txt',
             'r',encoding='utf-8')
except OSError:
    print('cannot open')
else:
    print(len(f.readlines()), 'lines')
    f.close()
3 lines

异常处理程序不仅处理 try 子句中遇到的异常,还处理 try 子句中调用(即使是间接地)的函数内部发生的异常。

如果 try 子句中有多个可能的异常,只处理最先引发的:

def f():
    return 3/0

try:
    f() * int('a')
except ValueError as v:
    print(v)
except  ZeroDivisionError as z:
    print(z)
division by zero