Python 3 非程式設計師教程/除錯
- "當我們開始程式設計時,我們驚訝地發現,讓程式正確執行並不像我們想象的那麼容易。除錯不得不被發現。我記得那一刻,我意識到,我餘生的很大一部分將花費在尋找我自己的程式中的錯誤上。" — Maurice Wilkes 發現除錯,1949 年
到目前為止,如果你一直在玩弄你的程式,你可能已經發現,有時程式會做一些你不想它做的事情。這很常見。除錯就是弄清楚計算機在做什麼,然後讓它做你想讓它做的事情的過程。這可能很棘手。我曾經花費近一週時間追蹤和修復一個錯誤,這個錯誤是由某人將一個 x 放置在應該放置 y 的位置引起的。
本章將比前面的章節更抽象。
首先要做的事情(這聽起來很明顯)是弄清楚程式在正確執行的情況下應該做什麼。想出一些測試用例,看看會發生什麼。例如,假設我有一個程式來計算矩形的周長(所有邊的長度之和)。我有以下測試用例
| 高度 | 寬度 | 周長 |
|---|---|---|
| 3 | 4 | 14 |
| 2 | 3 | 10 |
| 4 | 4 | 16 |
| 2 | 2 | 8 |
| 5 | 1 | 12 |
我現在在我的程式上執行所有這些測試用例,看看程式是否按照我的預期執行。如果它沒有,那麼我需要弄清楚計算機在做什麼。
更常見的情況是,一些測試用例會成功,而另一些則不會。如果是這樣的話,你應該嘗試找出哪些成功的用例具有共同點。例如,這裡是一個周長程式的輸出(你可以在一分鐘後看到程式碼)
Height: 3 Width: 4 perimeter = 15
Height: 2 Width: 3 perimeter = 11
Height: 4 Width: 4 perimeter = 16
Height: 2 Width: 2 perimeter = 8
Height: 5 Width: 1 perimeter = 8
請注意,它對前兩個輸入不起作用,它對接下來的兩個輸入起作用,而它對最後一個輸入不起作用。嘗試找出哪些成功的用例具有共同點。一旦你對問題有了一些瞭解,找到原因就更容易了。對於你自己的程式,如果你需要,你應該嘗試更多的測試用例。
接下來要做的事情是檢視原始碼。在程式設計過程中,最重要的工作之一就是閱讀原始碼。執行此操作的主要方法是程式碼走查。
程式碼走查從第一行開始,一直往下執行,直到程式結束。while 迴圈和 if 語句意味著某些行可能永遠不會被執行,而某些行會被執行多次。在每一行,你都要弄清楚 Python 做了什麼。
讓我們從簡單的周長程式開始。不要輸入它,你將閱讀它,而不是執行它。原始碼是
height = int(input("Height: "))
width = int(input("Width: "))
print("perimeter =", width + height + width + width)
- 問題:Python 執行的第一行是什麼?
- 答案:第一行總是第一個執行的。在本例中,它是:
height = int(input("Height: ")) - 那行做了什麼?
- 列印
Height:,等待使用者輸入一個字串,然後將字串轉換為一個整型變數 height。 - 接下來執行的是哪一行?
- 一般來說,它是下一行,即:
width = int(input("Width: ")) - 那行做了什麼?
- 列印
Width:,等待使用者輸入一個數字,並將使用者輸入的數字放到變數 width 中。 - 接下來執行的是哪一行?
- 當下一行與當前行的縮排程度不同時,它就是下一行,所以它是:
print("perimeter = ", width + height + width + width)(它也可能在當前行執行一個函式,但那是下一章的內容。) - 那行做了什麼?
- 首先它列印
perimeter =,然後它列印變數width和height中包含的值的總和,從width + height + width + width獲取。 width + height + width + width是否正確地計算了周長?- 讓我們看看,矩形的周長是底部(寬度)加上左側(高度)加上頂部(寬度)加上右側(嗯?)。最後一項應該是右側的長度,或者說是高度。
- 你明白為什麼有時周長會被“正確”地計算出來嗎?
- 當寬度和高度相等時,它被正確地計算出來了。
我們將要進行程式碼走查的下一個程式是一個應該在螢幕上列印 5 個點的程式。但是,這是該程式的輸出
. . . .
以下是該程式
number = 5
while number > 1:
print(".",end=" ")
number = number - 1
print()
這個程式的走查將更加複雜,因為它現在有縮排部分(或控制結構)。讓我們開始吧。
- 要執行的第一行是什麼?
- 檔案的第一行:
number = 5 - 它做了什麼?
- 將數字 5 放入變數 number 中。
- 下一行是什麼?
- 下一行是:
while number > 1: - 它做了什麼?
- 嗯,一般來說,
while語句會檢視它們的表示式,如果表示式為真,它們就會執行下一行縮排的程式碼塊,否則它們就會跳過下一行縮排的程式碼塊。 - 所以它現在做了什麼?
- 如果
number > 1為真,那麼接下來的兩行將被執行。 - 那麼
number > 1嗎? - 最後放入
number的值是5,而5 > 1,所以答案是肯定的。 - 那麼下一行是什麼?
- 由於
while為真,所以下一行是:print(".",end=" ") - 那行做了什麼?
- 列印一個點,由於存在額外的引數
end=" ",所以下一行列印的文字將不會在不同的螢幕行上。 - 下一行是什麼?
number = number - 1因為它是下一行,並且沒有縮排變化。- 它做了什麼?
- 它計算
number - 1,即number的當前值(或 5),從中減去 1,並將其作為number的新值。所以基本上它將number的值從 5 更改為 4。 - 下一行是什麼?
- 嗯,縮排級別降低了,所以我們必須檢視它是哪種控制結構。它是一個
while迴圈,所以我們必須回到while子句,即while number > 1: - 它做了什麼?
- 它檢視
number的值,即 4,並將其與 1 進行比較,由於4 > 1,所以 while 迴圈繼續。 - 下一行是什麼?
- 由於 while 迴圈為真,所以下一行是:
print(".",end=" ") - 它做了什麼?
- 它在該行上列印第二個點,以空格結尾。
- 下一行是什麼?
- 沒有縮排變化,所以它是:
number = number - 1 - 它做了什麼?
- 它取
number的當前值(4),從中減去 1,得到 3,最後將 3 作為number的新值。 - 下一行是什麼?
- 由於 while 迴圈結束導致了縮排變化,所以下一行是:
while number > 1: - 它做了什麼?
- 它將
number的當前值(3)與 1 進行比較。3 > 1,所以 while 迴圈繼續。 - 下一行是什麼?
- 由於 while 迴圈條件為真,所以下一行是:
print(".",end=" ") - 它做了什麼?
- 它在該行上列印第三個點。
- 下一行是什麼?
- 它是:
number = number - 1 - 它做了什麼?
- 它取
number的當前值(3),從中減去 1,並將 2 作為number的新值。 - 下一行是什麼?
- 回到 while 迴圈的開頭:
while number > 1: - 它做了什麼?
- 它將
number的當前值(2)與 1 進行比較。由於2 > 1,所以 while 迴圈繼續。 - 下一行是什麼?
- 由於 while 迴圈正在繼續:
print(".",end=" ") - 它做了什麼?
- 它發現了生命、宇宙和萬物的意義。我開玩笑的。(我必須確保你醒著。)這一行在螢幕上列印第四個點。
- 下一行是什麼?
- 它是:
number = number - 1 - 它做了什麼?
- 取
number的當前值(2),從中減去 1,並將 1 作為number的新值。 - 下一行是什麼?
- 回到 while 迴圈:
while number > 1: - 這一行做了什麼?
- 它將
number的當前值(1)與 1 進行比較。由於1 > 1為假(1 不大於 1),所以 while 迴圈退出。 - 下一行是什麼?
- 由於 while 迴圈條件為假,所以下一行是 while 迴圈退出後的下一行,即:
print() - 那行做了什麼?
- 讓螢幕進入下一行。
- 為什麼該程式沒有列印 5 個點?
- 迴圈提前退出 1 個點。
- 我們如何解決這個問題?
- 讓迴圈延遲 1 個點退出。
- 我們如何做到這一點?
- 有幾種方法。一種方法是將 while 迴圈更改為:
while number > 0:另一種方法是將條件更改為:number >= 1還有其他一些方法。
你需要弄清楚程式在做什麼。你需要弄清楚程式應該做什麼。找出兩者之間的差異。除錯是一項需要練習才能掌握的技能。如果你在一個小時後仍然無法解決問題,那就休息一下,和別人談談這個問題,或者沉思你肚臍裡的絨毛。過一會兒再回來,你可能會對這個問題有新的想法。祝你好運。