错误:
1、程序编写有问题造成的:即bug
2、用户输入造成的:需要检查输入
3、完全无法在程序运行过程中预测的:
磁盘满、网络断掉。这类错误也称为异常,在程序中通常是必须处理的,否则,程序会因为各种问题终止并退出。
解决办法
1、跟踪程序的执行,查看变量的值是否正确,这个过程称为调试。 2、Python的pdb可以让我们以单步方式执行代码。
错误处理
-
当我们认为某些代码可能会出错时,就可以用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except语句块,执行完except后,如果有finally语句块,则执行finally语句块,至此,执行完毕。
-
Python的错误其实也是class,所有的错误类型都继承自BaseException,所以在使用except时需要注意的是,它不但捕获该类型的错误,还把其子类也“一网打尽”。
使用try...except可以跨越多层调用
try:
print('try...')
r = 10 / int('2')
print('result:', r)
except ValueError as e:
print('ValueError:', e)# int()函数可能会抛出ValueError
except ZeroDivisionError as e:# 捕获除以零的时候的错误
print('ZeroDivisionError:', e)
else:
print('no error!')
finally:
print('finally...')
print('END')
发生了不同类型的错误,应该由不同的except语句块处理
当没有错误发生时,会自动执行else语句
使用try…except捕获错误还有一个巨大的好处,就是可以跨越多层调用,比如函数main()调用bar(),bar()调用foo(),结果foo()出错了,这时,只要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...')
调用栈
- 如果错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获,打印一个错误信息,然后程序退出。
- 出错的时候,一定要分析错误的调用栈信息,才能定位错误的位置
# err.py:
def foo(s):
return 10 / int(s)
def bar(s):
return foo(s) * 2
def main():
bar('0')
main()
执行,异常栈信息如下
$ python3 err.py
Traceback (most recent call last):
File "err.py", line 11, in <module>
main()
File "err.py", line 9, in main
bar('0')
File "err.py", line 6, in bar
return foo(s) * 2
File "err.py", line 3, in foo
return 10 / int(s)
ZeroDivisionError: division by zero
Python内置的logging模块
既然我们能捕获错误,就可以把错误堆栈打印出来,然后分析错误原因,
同时,让程序继续执行下去。
Python内置的logging模块可以非常容易地记录错误信息
# err_logging.py
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')
同样是出错,但程序打印完错误信息后会继续执行,并正常退出:
通过配置,logging还可以把错误记录到日志文件里,方便事后排查。
raise
抛出错误
错误是class,捕获一个错误就是捕获到该class的一个实例。因此,
错误并不是凭空产生的,而是有意创建并抛出的
# err_raise.py
class FooError(ValueError):
pass
def foo(s):
n = int(s)
if n==0:
raise FooError('invalid value: %s' % s)
return 10 / n
foo('0')
$ python3 err_raise.py
Traceback (most recent call last):
File "err_throw.py", line 11, in <module>
foo('0')
File "err_throw.py", line 8, in foo
raise FooError('invalid value: %s' % s)
__main__.FooError: invalid value: 0
捕获完毕,进行抛出
捕获错误目的只是记录一下,便于后续追踪。但是,由于当前函数不知道
应该怎么处理该错误,所以,最恰当的方式是继续往上抛,让顶层调用者去处理。
raise语句如果不带参数,就会把当前错误原样抛出。
# err_reraise.py
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()
调试
使用print()
def foo(s):
n = int(s)
print('>>> n = %d' % n)
return 10 / n
def main():
foo('0')
main()
使用断言
判断flase,assert语句本身就会抛出AssertionError:
def foo(s):
n = int(s)
assert n != 0, 'n is zero!'
return 10 / n
def main():
foo('0')
assert的意思是,表达式n != 0应该是True,否则,根据程序运行的逻辑,后面的代码肯定会出错。
如果断言失败,assert语句本身就会抛出AssertionError:
$ python err.py
Traceback (most recent call last):
...
AssertionError: n is zero!
启动Python解释器时可以用-O参数来关闭assert:
$ python -O err.py
logging
允许你指定记录信息的级别,有debug,info,warning,error等几个级别,这样一来,你可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息。
和assert比,logging不会抛出错误,而且可以输出到文件:
import logging
logging.basicConfig(level=logging.INFO)
#当我们指定level=INFO时,logging.debug就不起作用了。同理,指定level=WARNING后,debug和info就不起作用了
s = '0'
n = int(s)
logging.info('n = %d' % n)
print(10 / n)
$ python err.py
INFO:root:n = 0
Traceback (most recent call last):
File "err.py", line 8, in <module>
print(10 / n)
ZeroDivisionError: division by zero
调试器pdb
调试器pdb
启动-m pdb
$ python -m pdb err.py
输入1查看代码
输入n单步执行代码
p 变量名来查看变量
输入命令q结束调试
设置断点:在可能出错的地方放一个pdb.set_trace()
可以用命令p查看变量,或者用命令c继续运行:
IDE
单元测试
- 单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
- 比如对函数abs(),我们可以编写各种数值测试这个函数
未完
文档测试
未完