自学Python之路 第八章 错误、调试和测试

自学Python之路

四川农业大学——大数据实验室第三次打卡

第八章

8.1 错误

·在程序运行过程中,总会遇到各种各样的错误,如下:
1.程序编写有问题:比如本来应该输出整数结果输出了字符串,这种错误我们通常称之为bug,bug是必须修复的。
2.用户输入错误:比如让用户输入email地址,结果得到一个空字符串,这种错误可以通过检查用户输入来做相应的处理。
3.无法预测的错误(异常):比如在写入文件的时候,磁盘满了,写不进去了,或者从网络抓取数据,网络突然断掉了。这类错误也常称之为异常,在程序中通常是必须处理的,否则,程序会因为各种问题终止并退出。
·这一章节将学习Python内置的一套异常处理机制,来帮助我们进行错误处理。此外,我们也需要跟踪程序的执行,查看变量的值是否正确,这个过程称为调试,Python的pdb可以让我们以单步方式执行代码。最后,编写测试也很重要。有了良好的测试,就可以在程序修改之后反复运行,确保程序输出符合我们编写的测试。

8.2 错误处理

·许多高级语言通常都内置了一套try…except…finally…的错误处理机制,Python也不例外。

·可以看到,从下面的代码可以看到,在计算10 / 0时会产生一个除法运算错误,从输出可以看到,当错误发生时,后续语句print(‘result’,r)不会被执行,except由于捕获到ZeroDivisionError,因此被执行。最后,finally语句被执行。
在这里插入图片描述
·当我们把0改成2时,从下图的输出可以看到由于没有错误发生,所以except不会被执行,但是finally如果有,则一定会被执行。
在这里插入图片描述
·如果发生了不同类型的错误,应该由不同的except语句处理,如下:
在这里插入图片描述
·此外,如果没有错误发生,可以在except语句块后面加一个else,当没有错误发生时,会自动执行else语句,如下:
在这里插入图片描述
·Python的错误其实也是class,所有的错误类型都继承自BaseException,所以在使用except时要注意的是,它不但捕获该类型的错误,还把其他子类也“一网打尽”。第二个except永远也捕获不到UnicodeError,因为UnicodeError是ValueError的子类,如果有,也被第一个except给捕获了,如下:

try:
    foo()
except ValueError as e:
    print('ValueError')
except UnicodeError as e:
    print('UnicodeError')

·Python所有的错误都是从BaseException类派生的。使用try…except捕获错误还有一个巨大的好处就是可以跨越多层调用,也就是说,不需要在每个可能出错的地方都去捕获错误,只要在合适的层次去捕获错误就可以了,这样就大大减少了try…except…finally…的麻烦。比如函数main()调用foo(),foo()调用bar(),结果bar()出错了,这是,只要main()捕获到了,就可以处理:
在这里插入图片描述
·调用栈堆:如果错误没有被捕获,它就会一直往上抛,最后被Python解释器捕获,打印一个错误信息,然后程序退出来(下图根据错误类型ZeroDivisionError,可以看出int(s)本身并没有出错,但是int(s)返回0,在计算10 / 0时出错):
在这里插入图片描述
·记录错误:Python内置的logging模块可以非常容易地记录错误信息从下图可以看到,同样是出错,但程序打印完错误信息后会继续执行,并正常退出(通过配置,logging还可以把错误记录到日志文件里,方便时候排查):
在这里插入图片描述
·抛出错误:因为错误是class,捕获一个错误就是捕获到该class的一个实例。因此,错误不是凭空产生的,而是有意创建并抛出的。Python的内置函数会抛出很多种类型的错误,我们自己编写的函数也可以抛出错误。
·如果要抛出错误,首先根据需要,可以定义一个错误的class,选择好继承关系,然后用raise语句抛出一个错误的实例:
在这里插入图片描述
·raise语句如果不带参数,就会把错误原样抛出。此外,在except中raise一个Error,还可以把一种类型的错误转换为另外一种类型(只要是合理的转换逻辑就可以,但是绝不可以把一个IOError转换成毫不相干的ValueError:
在这里插入图片描述

小结

·Python内置的try…except…finally用来处理错误十分方便。出错时,会分析错误信息并定位错误发生的代码位置才是最关键的。
·程序也可以主动抛出错误,让调用者来处理相应的错误。

8.3 调试

·调试方法:1.最简单粗暴的方法,直接用print()把可能有问题的变量打印出来(但是其最大的坏处就是还得把print()删除):
在这里插入图片描述
2.断言:凡是用print的地方都可以用断言(assert)来代替,assert的意识是:表达式n != 0应该是true,否则后面的代码肯定会出错。如果断言失败,assert语句本身就会抛出AssertionError(但是程序中如果处处有assert,和print相比也好不到哪儿去。不过启动Python解释器时可用-0参数来关闭assert):
在这里插入图片描述
3.logging:把print()换为logging,和assert相比,logging不会抛出错误,而且可以输出到文件。logging的好处还在于它允许你指定记录信息的级别,有debug,info,warning,error等几个级别,当我们指定level=INFO时,logging.debug就不起作用了。同理,指定level=WARNING后,debug和info就不起作用了。这样一来,就可以放心地输出不同级别的信息,也不用删除,最后统一控制输出哪个级别的信息:
在这里插入图片描述

4.pdb:启动Python的调试器pdb,让程序以单步方式运行,可以随时查看运行状态。(暂时还没理解透彻,就先不写这个了 /哭了)
5.pdb.set_trace():这个方法也是用pdb,但是不需要单步执行,只需要import pdb,然后在可能出错的地方放一个pdb.set_trace(),就可以设置一个断点:
在这里插入图片描述

8.4 单元测试

在这里插入图片描述
在这里插入图片描述
·setUp与tearDown:可以在单元测试中编写两个特殊的setUp()和tearDown()方法。这两个方法分别在每调用一个测试方法的前后分别被执行。这两个方法的作用是可通过setUp()方法连接数据库,在tearDown()方法中关闭数据库,这样就不必在每个测试方法中重复相同的代码:
在这里插入图片描述

8.5 文档测试

·python中内置的“文档测试”(doctest)模块可以直接提取注释中的代码并执行测试。doctest严格按照python交互式命令行的输入和输出来判断测试结果是否正确。只有异测试异常的时候,可以用…表示中间一大段烦人的输出:
在这里插入图片描述

小结

·doctest非常有用,不但可以用来测试,还可以直接作为示例代码。通过某些文档生成工具,就可以自动把包含doctest的注释提取出来。用户看到文档时,同时也就看到了doctest。

猜你喜欢

转载自blog.csdn.net/EMIvv/article/details/106021088