1.1 错误处理
1.1.1 try
>>> try:
... print('try...')
... r = 10 / 0
... print('result:', r)
... except ZeroDivisionError as e:
... print('except:', e)
... finally:
... print('finally...')
... print('END')
...
try...
except: division by zero
finally...
END
当我们认为某些代码可能会出错时,就可以用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except语句块,执行完except后,如果有finally语句块,则执行finally语句块,至此,执行完毕。
finally语句块无论是否执行except,都会被执行的。
同时定义多个except
>>> try:
... print('try...')
... r = 10 / int('a')
... print('result:', r)
... except ValueError as e: --异常1
... print('ValueError:', e)
... except ZeroDivisionError as e: --异常2
... print('except:', e)
... else: --无异常时执行else
... print('no error')
... finally:
... print('finally...')
... print('END')
...
try...
ValueError: invalid literal for int() withbase 10: 'a'
finally...
END
跨越多层调用
使用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...')
...
>>> main()
Error: division by zero
finally...
只要在合适的层次去捕获错误就可以了。
1.1.2 调用堆栈
[root@daidai python]# cat err.py
#!/bin/bash/python
# -*- coding:utf-8 -*-
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
bar('0')
main()
[root@daidai python]# python err.py
Traceback (most recent call last):
File "err.py", line 13, in <module>
main()
File "err.py", line 11, in main
bar('0')
File "err.py", line 8, in bar
return foo(s) * 2
File "err.py", line 5, in foo
return 10 / int(s)
ZeroDivisionError: division by zero --最后便是错误的根源
1.1.3 记录错误
捕捉错误,把错误堆栈记录,让程序继续运行。
Python内置的logging模块可以非常容易地记录错误信息。
[root@daidai python]# cat err_logging.py
#!/usr/bin/python
# -*- coding:utf-8 -*-
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')
[root@daidai python]# python err_logging.py
ERROR:root:division by zero
Traceback (most recent call last):
File "err_logging.py", line 14, in main
bar('0')
File "err_logging.py", line 10, in bar
return foo(s) * 2
File "err_logging.py", line 7, in foo
return 10 / int(s)
ZeroDivisionError: division by zero
END
1.1.4 抛出错误
尽量使用python内置的错误类型。
错误是class,捕获一个错误就是捕获到该class的一个实例。
定义一个错误class,选择好继承关系。
>>> class FooError(ValueError):
... pass
...
>>> def foo(s):
... n = int(s)
... if n == 0:
... raise FooError('invalid value: %s' % s)
... return 10 / n
...
>>> foo('0')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in foo
__main__.FooError: invalid value: 0 --自己定义的错误
另一种错误处理方式——错误上抛
>>> def foo(s):
... n = int(s)
... if n == 0:
... raise ValueError('invalid value: %s' %s) --python内置错误
... return 10 /n
...
>>> def bar():
... try:
... foo('0')
... except ValueError as e:
... print('ValueError')
... raise --raise语句如果不带参数,就会把当前错误原样抛出。
...
>>> bar()
ValueError
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in bar
File "<stdin>", line 4, in foo
ValueError: invalid value: 0
其实这种错误处理方式不但没病,而且相当常见。捕获错误目的只是记录一下,便于后续追踪。但是,由于当前函数不知道应该怎么处理该错误,所以,最恰当的方式是继续往上抛,让顶层调用者去处理。好比一个员工处理不了一个问题时,就把问题抛给他的老板,如果他的老板也处理不了,就一直往上抛,最终会抛给CEO去处理。
在except中raise一个Error,还可以把一种类型的错误转化成另一种类型
>>> try:
... 10 / 0
... except ZeroDivisionError:
... raise ValueError('input error')
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero
During handling of the above exception,another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
ValueError: input error