从零开始的Python计划#6.1【程序调试】


🌟🌟🌟很久没更新啦!有被老师布置的烧脑实践困住了hh,如果有人也想要练习的话,可以私聊我发给你www
这章我们将讨论调试,如何确保能够更好地理解程序中的问题,以及如何在问题发生时修复它们。
我们可以把问题分为三个部分:(1)语法错误(在写代码时犯的错误),检查代码错误,看到错误和问题在哪儿的同时修复编译错误。 (2)运行错误,理解运行时错误并理解Python提供给我们的异常跟踪,Python说得很清楚,但有时候我们需要非常小心才能弄清楚问题是从哪里来的,我们怎样才能回到那个地方,确保那里的代码按照我们想要的方式运行。 (3)逻辑错误,关于结构,计划,测试,断点等,试图了解它们是什么以及如何在我们的开发中发挥作用。还有如何观察变量的行为,观察它们是否按照期望行事,或者是否是它们导致了问题的产生。 用spider提供的IDLE调试器(集成开发和学习环境)(我自己本身是mac用户,然后用的是Anconda-Navigator里面的spider来做python的练习的,所以一样的话可能更好理解)(4)测试台设计,基本上是一些案例研究,看看如何发现问题,为什么这样做,以及我们如何利用所学的知识来解决代码中的问题。

调试简介

调试意味着识别和修复程序中的错误或bug,有时术语bug也被用来指在其他地方必须被发现和修复的错误,bug(虫子)这个名字可能源于一个硬件错误,这个错误是由格雷斯·霍珀在20世纪40年代发现并拍摄的一台早期计算机的继电器中发现的一只虫子造成的。(这些感觉无关紧要hhh)
总之,软件中的问题称为bug,另一件非常重要的事情是每个程序都有错误,所以不用担心,虫子是正常的,调试是为了让程序变得更好更快的。

错误类型

语法错误
常见的语法错误可能是不正确的缩进、缺少元素、拼写错误等。这类最容易找到,可以通过代码检查或解释器在编译时报告来修好。当我们有语法错误时,编译器会对发生的事情非常详细,并且会显示出第几行发生了什么样的错误。

运行错误
可能在编译时不被注意,只在运行时才显示。这个错误有点难找,因为一开始,如果我们不探索程序在程序中建立的所有路径,我们可能找不到运行时错误。

逻辑错误
这个通常是最难找到的。可以通过观察程序行为来识别。需要花点时间来识别和处理逻辑错误

语法错误

(1)代码检查

当Python解释器试图编译您的程序时,它会发现这些类型的错误,并在不运行任何东西的情况下以错误消息退出;
语法错误是使用Python语言时的错误;(这些就像在命名中使用特殊函数时犯的简单错误一样)
在试图编译之前,最好先对代码进行彻底的视觉检查。
–—检查布局/缩进(Python对布局和缩进非常挑剔,所以在我们的循环和函数里一切都要缩进,并且布局需要清晰易懂。)
–—检查标点符号(标点符号也要注意,函数有冒号:,字典有冒号:,我们可以使用point(.)调用一个方法等等,小心标点符号,这也很关键)
–—检查拼写错误(如果有东西错了,你不知道为什么错,那可能是拼写错误。确保在声明函数或变量时,确定没有拼错)

检查布局/缩进

Python解释器使用缩进标记块的开始和结束;
Python中的一些块控制关键字是def、for、if、else、elif、try、except等。

举个例子:
我们定义一个叫做test的函数,这段代码缩进得很奇怪
Python会不能理解这个程序,总之一团糟
def test():
		for year in [2000,1903,2008,2009]:   #【for循环缩进了两次】
    if(isleap(year)):
       	   isornot="  #缩进了两次
              else:   #应该跟if对齐
    isornot="not "
        print(str(year)+" is "+isornot+"a leap year“)
        
当每次输入块控制关键字时缩进一次的时候,都需要小心

def test():
	for year in [2000,1903,2008,2009]:
    	 if(isleap(year)):
       		 isornot=""
         else:
        	 isornot="not "
    	 print(str(year)+" is "+isornot+"a leap year“)
这样子处理之后,看起来就干净很多
所以缩进中的三个小变化对代码的输出是会产生巨大影响的
检查标点符号

括号和引号(()[] {}“’’)必须成对出现,引号有单‘’和双“”
块控制语句def、if、for、while等必须以冒号:结尾
反斜杠\ 或 百分比转义符%可以将字符转换为其他字符如果你自己使用反斜杠\用成了/,就会出现问题,要小心这些特殊字符。
使用逗号,分隔列表项。

def test( : #少了一个括号
	for year in [2000.1903'2008,2009 };  #2000后应该是逗号而不是句号,1903同理,这个列表应该是方括号而不是花括号,结尾应该是冒号而不是分号
		if (isleap(year):  #少一个右括号
			isornot='"    #应该全部使用单引号或者双引号
		else  #少了一个冒号:
        	isornot="not \"    #\错误,应该移除 	
        	print(str(year)+" is " &isornot+"a leap year“)       
        	#&要移除,”是错误的,应该改为"
所以一共有10处错误


再继续看例子
def test():
	for y in [2000,1903,2008,2009]:
    	if(isleap(year)):
        	isornot=""
        else:
            isorno="not "  #isorno拼写错误
        print(str(yaer)+" is "+issornot+"a leap year")#yaer拼写错误
Test()#定义的函数是test,Python区分大小写。

定义的函数名需要根据对它的所有调用进行检查。
我们可以使用CTRL+f找到(mac用户是command+f)和 再找一次CTRL+g(mac是command+g)一致地检查并遍历同一函数的出现,(这非常方便,你可以找一个词并且看看这个词每次出现的拼写是否正确)

修复编译错误

编译器将报告代码中检测到阻止编译的错误的位置,我们的编译器不仅会告知我们发生了什么,也会告诉出错的地方。

语法错误,无效语法 invalid syntax
错误可能在代码中检测到错误的位置。在这里插入图片描述
或者真正的错误可能在代码中编译器检测错误的位置之前。
意味着前一个语句可能包含错误
在这里插入图片描述
else是正确的,错误在上一句少了右括号
尽管python有时很精确,我们仍然 需要仔细的目测

所以有时候它为我们提供了最接近的语法错误

运行时错误

如果程序语法正确(即没有语法错误),它将由Python解释器运行,但是,如果在执行过程中遇到运行时错误,程序可能会意外退出。
Python运行时错误的一些示例:
除以零
––对不完整的不兼容类型执行操作(比如字符串加上整数)
––使用尚未定义的变量
––访问的元素或对象属性列表根本不存在
––尝试访问不存在的文件

(1)读取运行时异常回溯

1,在运行时引发异常时发生的回溯可以帮助我们定位和识别导致崩溃的错误,但我们还是需要仔细观察检查和理解,了解问题所在。
2,可以通过仔细的编码和异常处理来处理。
所以我们可以通过更聪明的编码和异常处理来避免运行时错误(比如,当我们要求用户输入一个大于2的整数,如果数字小于或等于2,我们可以使用while循环,并不断要求用户输入我们想要的数字)

逻辑错误

逻辑错误是最难纠正修复的,当程序运行时没有崩溃,但是会产生错误的结果。这个错误通常是由程序逻辑中的一个错误引起的。(在某种程度上是错误的,但不至于错到让它崩溃,但是会获得错误的结果)因此,不会收到错误消息,因为没有发生语法或运行时错误。必须通过自己检查代码的所有相关部分来发现问题,然后试着修复它。
关于导致逻辑错误的例子:
––使用错误的变量名
––向水平方向缩进块(让print在for循环之外和在for循环之内可能会有完全不同的结果)
––错误的数学计算
––使误认为是布尔表达式
或我们说if什么是真的,但事实上这件事情不是真的。程序都不会崩溃,但是结果都不是我们所期望的。

(1)断点

断点是一个源代码行,当我们正在进行错误检测时,我们会暂时暂停正在运行的程序。 当我们设置断点时,运行程序,我们的程序将在断点处停止,直到我们告诉程序重新开始运行。
如果程序在断点之前已经正确地完成了所有操作,那么错误必须在断点之后。 就在断点之前,我们可以打印一些东西,确保一切正常,如果一切都是对的直到那个断点(直到断点一切正常),错误一定在代码的最后部分。
然后我们可以设置另一个断点,并检查正在搜索的错误是否发生(然后继续,直到我们将它缩小到特定的行或特定的代码为止)在两个断点之间,以缩小搜索空间。

(2)IDLE调试器

1)IDLE有一个内置的调试器,允许在每个语句之后分析程序的状态
这是一个提供给我们的工具,可以帮助正在尝试学习Python的人一步一步地跟踪程序和变量来确保一切正常。
2)IDLE调试器使我们可以一次进入执行一个源代码行的功能(所以我们可以进入函数并检查工作情况)根据需要跳过这些或离开它们,模拟浏览代码或者可以正常运行程序,从IDLE调试器那里观察,我们可以观察到变量的变化。 我们这样做是为了在程序崩溃时看到所有变量的变化,当什么想找出程序为什么出错时,这是一个非常好的办法。
3)如果我们将程序模块化为许多小函数,这将使我们在调试器中运行程序时能够更灵活地控制程序。 因为在一个有很多函数的程序中,函数使用调试器来划分代码,这使得我们更容易探测函数并查看变量的范围,可以快速了解问题所在。
4)在调试模式下启动程序
––在shell窗口中,我们必须用File>Open打开一个新的Python脚本
––选择Debug>Debugger来启动调试控件
––在源程序窗口中运行一个模块

这个是它的样子:
这是来自Windows的老截图,如果是mac用户,看起来会很不一样

会有一系列的按钮和选项go,step,over,out…
按钮:
Go:运行程序直到下一个断点 (所以我们可以设置一系列断点并按下go键,让程序只运行这段代码来)要设置断点,右键单击一行,然后选择 “set breakpoint” 这个可以在代码视图下完成
Step:执行下一条语句,可以通过python脚本一步一步地尝试分析并找出发生了什么。如果下一个语句是函数调用,则调用执行将进入该函数
Over:与step步骤相同,除非下一个语句是函数调用,否则它不会跨入函数。 所以over可以跳过函数(在我知道这个函数式有效的情况下,并且我想跳过这个函数,可以点击over按钮)
Out:完成当前功能 如果我进入一个函数,我觉得它能运作,我可以选out,它会快速运行并离开。

选项:
Stack:显示当前运行的函数
Souse:在源文件中显示当前语句
Locals:显示局部变量以及值 (locals允许我们处理函数中所有的局部变量和它们的值,所以我们可以看到一个变量什么时候有问题或者为什么我有一个特定的数字而不是另一个。)
Globals:显示全局变量及其值

Spyder调试器-监视运行时变量值
在这里插入图片描述
下面的那个就是spyder调试器,左上角是代码
你可以看到调试器是如何帮助我们看到第3行的,(右上角)调试器告诉我有两个变量,num是int整数变量,option是str字符串变量…
你会看到它是多么实用,不仅能够写代码,还能看到输出,可以一步一步看变量是如何运行的,我的函数是如何运行的,值是如何变化的。

IDLE调试器断点是非常有用的工具,可以帮助我们修复存在的问题和逻辑错误。你越习惯在脚本中实现它们,你越能成为一个不仅仅是编写复杂的python脚本的人,同时也立即发现错误以及错误发生的原因。

测试用例

接下去我们将讨论测试以及如何创建自己的测试,然后我们会通过一个小的例子研究,可以看到如何执行实际的测试。
测试用例是执行软件测试的基本组件。(我们创建它是为了测试和探测我们自己的python脚本)它以最普遍的形式指定实际输入值和预期输出值。(不仅传递输入,也能预期输出值,可以比较输入值和预期的输出值)确定执行测试用例的任何约束条件(例如处理器速度、可用内存)。(我们现在的脚本都非常小,如果希望它能运行得更快,但是它跑得很慢,用用户测试来测试这个,可以看看是否有问题)
测试用例执行:是执行软件系统的过程(有一个叫做文本测试用例执行的东西,当我们接受我们刚刚创建的测试时,可以运行它确保它做我们的脚本应该做的)
4个运行测试用例的特性:
––需要在测试用例中指定的约束下运行测试用例
––使用测试用例中指定的输入
––观察结果并将其与测试用例所指定的结果进行比较
––如果观察结果与预期结果不同,则检测到故障
(比如当我说我的脚本是两个数字的总和时,如果我加上这些数字,我应该得到输出4,但是收到的结果是5,这说明测试失败,我的脚本表现得不对)

(1)测试用例设计

测试不能保证所有的缺陷都不存在,测试非常有限,因此很难使用测试来消除代码中的所有错误和问题。所以测试用例设计对于使测试尽可能完整至关重要。
设计测试用例的基本方法取决于两个因素。1·软件规范(黑盒测试)2·软件规范和实现软件的代码(白盒测试)(白盒测试发生在我是脚本本身的开发人员的时候,我不仅知道脚本是如何从外部运作的,也能预期一些结果表现,所以能创建一些好的测试)
测试用例设计应该考虑很多问题:
––等价分区
––边值分析
––声明范围
––决策/条件覆盖

1·等价划分

等价划分是当可能的输入可以是非常大和无限大时,不可能全部测试。 当我们需要选择一定数量的输入来描述程序可以拥有的所有无限输入时,等效分区就会起作用。
将一组测试条件划分为可以认为是相同的分区。(所以即使有无限的输入,我可以用某种方式划分数字或输入,我只能用一个来代表更大的群体)
然后我们从测试的分区中取一个值(每个分区选择一个值)。如果那个值能通过测试,同一组的所有其他值都将通过测试。同样,如果那个值失败了,则此组中的所有值都将失败。

看个例子:
在预订新航班时考虑机票在航班预订应用程序中的行为
票面价值从1到10视为有效,当值11到99被认为对保留无效时,将出现错误:“一次只能订购十张票”。
看一下图表:
在这里插入图片描述> 0或0以下无效,1-10有效,11-99无效,100无效
可以看出测试分成了四部分。(划分分区)
只分别选四个数字做每个分区的代表,就能够彻底测试我们的脚本

在这里插入图片描述

2·边值分析

从有效和无效的边界值选择输入,选择产生输出边界值的输入,就是边值分析的作用。
测试用例是根据在先前的等价分区测试中确定的分区之间的边界值设计的。
-----从边界值中选择输入(进入分区的边界,在这两个分区之间选择一个输入)
-----选择一个输入来产生边界值(可以随机输入或者不随机输入,只要选择了一个输入,它就会给我一个边界值作为输出。所以这不仅仅是在测试一个通用分区还有分区的边界和确保了包括极限情况)

继续看例子:
在这里插入图片描述
界限是0到1,在有效和无效间;10-11在有效间和无效间;99-100…

3·代码覆盖率&决策/条件覆盖

代码覆盖率:测试用例如何完全执行软件系统的代码(我的测试也许可以运行,测试所有的脚本的功能,但不能运行脚本上所有的代码行。所以要确认所有的测试运行了所有代码)
应该尝试使测试用例进入和退出每个模块/方法/程序(每个方法,每一个步骤,写的东西都需要测试,否则就没有拥有它的意义了)
执行每一行代码(可能有些多余的东西,但是我们需要确保我们的测试通过了所有的代码行,确保写的每件事情都运作)
需要通过软件遵循每一个逻辑和决策路径(如果有True,Flase或if,else语句的话,确保测试了所有选项,这样才是一个完整的测试)
决策/条件覆盖:项目决策中的每一个条件都至少采取了一次所有可能的结果。(可以跟上面的代码覆盖率联系起来,如果有一个if-else语句,确保测试语句的所有可能结果,这样它就可以在所有分支中运行,并在if语句中运行所有语句块至少一次)
计划中的每一个决定都至少采取了一次所有可能的结果。(确保在设计运行程序的测试时,你的测试会测试一切)

(2)测试案例研究(1–5)

假设我们有一个简单的程序:程序员开发了一个软件,允许用户查看、创建和编辑联系人和联系人列表。(创建一个列表,呈现列表,并让我们添加和删除)
程序启动后,它将加载所有现在的联系人,然后查看如下所示的基本菜单:
在这里插入图片描述
用户可以v查看联系人,a添加联系人,d删除联系人,q放弃
那么我们如何来测试这个程序呢?
第一种测试情况:
在这里插入图片描述
1·我们需要一个查看联系人列表的测试用例,它的工作是可视化地显示用户的联系人
2·用户运行程序。出现带有四个选项的主菜单。用户输入选项:v
3·预期的结果是:保存在程序中的所有现有联系人都是可视化的
4·实际输出:程序中的所有现有联系人都将可视化
测试显然通过,有一个输入并且具有预期输出,且预期输出和实际输出相符合

第二种情况:在这里插入图片描述
1·想添加一个新联系人
2·用户运行程序,主菜单显示四个选项。要求用户输入a,系统将提示用户输入姓名和完整号码以查看联系人。
3·预期结果:将新联系人添加到列表中,并且一旦选择了选项查看联系人列表,用户就应该能够查看。一旦添加完联系人,回到菜单按v,就像测试一,用户能看到新的联系人列表和新联系人
因为预期产出与实际输出匹配,测试通过。

第三种情况:在这里插入图片描述
1·删除现有联系人
2·要求用户打开一个程序并且让他们选d-删除联系人。他们会被要求把联系人的索引删除
3·预期输出:如果用户运行查看联系人列表,会有一个没有Alice(上一步输入1)的新联系人名单。事实上,如果我们要求用户打印联系人列表,打印出来的结果没有Alice。
预期输出和实施输出相符,测试通过

情况四:
在这里插入图片描述
1·删除不存在的联系人(我们需要设计只测试不应该运作的东西的测试,因为也许不该运作的东西会破坏我们的代码。我们需要测试,哪怕用户做了什么错误的事情,而代码能去处理,并是否能从崩溃里恢复)
2·用户运行程序,主菜单显示4个选项,我们要求他们再次选择d,用户将被要求提供被删除的索引(输入10),列表中只有三个联系人可使用,我们只有3个,10不存在
3·用户收到警告消息“索引号无效”,所以这个程序不会崩溃,用户试图执行的操作无效。
4·实际结果:软件崩溃时出现索引错误
测试失败

测试五:
在这里插入图片描述
1·退出系统
2·要求用户运行程序,菜单显示4选项。要求用户这次输入q
3·预期:系统将在没有问题的情况下关闭
4·实际:系统关闭没有问题
测试通过

终于更新完了!有人催了我好久www,东西有点多,都是概念的问题,慢慢消化就好啦!学会调试是走上优秀程序员的必经之路哦!www🌟

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章