從零開始的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🌟

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