try
让我们用一个例子来看看 try 的机制:
try:
print('try…')
r = 10 / 0
print('result:', r)
except ZeroDivisionError as e:
print('except:', e)
finally:
print('finally…')
print('END')
当我们认为某些代码可能会出错时,就可以用 try 来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即 except 语句块,执行完 except 后,如果有 finally 语句块,则执行 finally 语句块,至此,执行完毕。
上面的代码在计算 10 / 0 时会产生一个除法运算错误:
try…
except: division by zero
finally…
END
从输出可以看到,当错误发生时,后续语句 print('result:', r) 不会被执行, except 由于捕获到 ZeroDivisionError ,因此被执行。最后, finally 语句被执行。然后,程序继续按照流程往下走。
如果把除数 0 改成 2 ,则执行结果如下:
try…
result: 5
finally…
END
由于没有错误发生,所以 except 语句块不会被执行,但是 finally 如果有,则一定会被执行(可以没有 finally 语句)。
你还可以猜测,错误应该有很多种类,如果发生了不同类型的错误,应该由不同的 except 语句块处理。没错,可以有多个 except 来捕获不同类型的错误:
try:
print('try…')
r = 10 / int('a')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
finally:
print('finally…')
print('END')
int() 函数可能会抛出 ValueError ,所以我们用一个 except 捕获 ValueError ,用另一个 except 捕获 ZeroDivisionError 。
此外,如果没有错误发生,可以在 except 语句块后面加一个 else ,当没有错误发生时,会自动执行 else 语句:
try:
print('try…')
r = 10 / int('2')
print('result:', r)
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally…')
print('END')
Python的错误其实也是class,所有的错误类型都继承自 BaseException ,所以在使用 except 时需要注意的是,它不但捕获该类型的错误,还把其子类也“一网打尽”。比如:
try:
foo()
except ValueError as e:
print('ValueError')
except UnicodeError as e:
print('UnicodeError')
第二个 except 永远也捕获不到 UnicodeError ,因为 UnicodeError 是 ValueError 的子类,如果有,也被第一个 except 给捕获了。
Python所有的错误都是从 BaseException 类派生的,常见的错误类型和继承关系看这里:
https://docs.python.org/3/library/exceptions.html#exception-hierarchy
使用 try…except 捕获错误还有一个巨大的好处,就是可以跨越多层调用,比如函数 main() 调用 foo() , foo() 调用 bar() ,结果 bar() 出错了,这时,只要 main() 捕获到了,就可以处理:
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
try:
bar('0')
except Exception as e:
print('Error:', e)
finally:
print('finally…')
也就是说,不需要在每个可能出错的地方去捕获错误,只要在合适的层次去捕获错误就可以了。这样一来,就大大减少了写 try…except…finally 的麻烦。