Python中的异常处理

异常处理

Python中使用try...except...finally...来处理异常。简单的栗子:

1
2
3
4
5
6
try:
print('try...')
except ZeroDivisionError as e:
print('except:', e)
finally:
print('finally...')

如果某段代码出现错误,可以使用try来包含这段代码,如果执行出错,则后续代码则不会被执行,而是直接跳转到错误处理代码,即except语句块。如果有finally语句块,则执行finally语句块。

在异常处理的流程当中,try语句中如果没有错误,则except一定不会被执行,如果有finally语句块(也可以没有),一定会会执行finally语句块。

可以有多个except语句,来处理不同类型的错误,如下面int()函数可能会抛出ValueError错误,所以使用except捕获ValueError。如果没有错误发生,可以再except语句块后面添加一个else,当没有错误发生时,自动执行else语句:

1
2
3
4
5
6
7
8
9
10
11
try:
r = 10 / int('2')
except ValueError as e:
print('ValueError:', e)
except ZeroDivisionError as e:
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally...')
print('END')

Python的错误类型都继承自BaseException,它不仅仅能捕获该类型错误,还能将其子类也捕获。比如:

1
2
3
4
5
6
try:
foo()
except ValueError as e:
print('ValueError')
except UnicodeError as e:
print('UnicodeError')

第二个except永远也不会执行,因为UnicodeErrorValueError的子类。该错误只能被第一个except捕获。官方文档描述了有关错误类型的继承关系。

try...except...可以跨多层调用,比如下面:

1
2
3
4
5
6
7
8
9
10
11
12
13
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...')

不需要再每个地方都捕获错误,只要在合适的层次去捕获错误就可以了。

如果没有捕获错误,它就会一直上抛,最后被Python解释器捕获,打印一个错误信息,然后程序退出。

Python内置的logging模块可以非常容易的记录错误信息,同时,让程序继续执行下去。程序打印完错误信息后会继续执行,并正常退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import logging

def foo(s):
return 10 / int(s)

def bar(s):
return foo(s) * 2

def main():
try:
bar('0')
except Exception as e:
logging.exception(e)

main()
print('END')

log:

1
2
3
4
5
6
7
8
9
10
11
$ python3 err_logging.py
ERROR:root:division by zero
Traceback (most recent call last):
File "err_logging.py", line 13, in main
bar('0')
File "err_logging.py", line 9, in bar
return foo(s) * 2
File "err_logging.py", line 6, in foo
return 10 / int(s)
ZeroDivisionError: division by zero
END

抛出错误:Python内置函数会抛出很多错误,也可以自己编写函数抛出错误。可以定义一个错误的class,选择继承关系,使用raise语句抛出一个错误的实例:

1
2
3
4
5
6
7
8
9
10
class FooError(ValueError):
pass

def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return 10 / n

foo('0')

另外一种处理错误的方式:在捕获错误的时候,打印ValueError!后,又将错误抛出。这是由于这个函数不知道怎么处理这个错误,所以,最恰当的方式是继续上抛,让顶层调用者去处理。raise语句如果不带参数,就会把当前的错误按照原样抛出。此外还可以将except中的raise一个Error,将一种错误转换成另外一种类型:raise ValueError('input error!')

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def foo(s):
n = int(s)
if n==0:
raise ValueError('invalid value: %s' % s)
return 10 / n

def bar():
try:
foo('0')
except ValueError as e:
print('ValueError!')
raise

bar()