非程式設計師的 Python 2.6 除錯教程
- "在我們開始程式設計的時候,我們驚訝地發現讓程式正常執行並不像我們想象的那樣容易。除錯不得不被發現。我記得我意識到我以後生命中的很大一部分將會花在尋找自己程式中的錯誤的那一刻。" — 莫里斯·威爾克斯 發現除錯, 1949
到目前為止,如果你一直在使用你編寫的程式,你可能已經發現有時程式會做一些你不想它做的事情。這是相當常見的。除錯是弄清楚計算機在做什麼,然後讓它做你想讓它做的事情的過程。這可能很棘手。我曾經花了將近一週的時間追蹤和修復一個錯誤,這個錯誤是由某人在應該使用 y 的地方輸入了 x 引起的。
本章將比之前的章節更抽象。
第一步(這聽起來很明顯)是弄清楚如果程式正常執行它應該在做什麼。想出一些測試用例並看看會發生什麼。例如,假設我有一個程式用來計算矩形的周長(所有邊的長度之和)。我有以下測試用例
| 高度 | 寬度 | 周長 |
|---|---|---|
| 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 = input("Height: ")
width = input("Width: ")
print "perimeter =", width + height + width + width
- 問題:Python 執行的第一行是什麼?
- 答案:第一行總是先被執行。在本例中,它是:
height = input("Height: ") - 那行程式碼做了什麼?
- 列印
Height:,等待使用者輸入一個數字,並將該數字放入變數 height 中。 - 下一行是什麼?
- 一般來說,它是下一行,也就是:
width = input("Width: ") - 那行程式碼做了什麼?
- 列印
Width:,等待使用者輸入一個數字,並將使用者輸入的內容放入變數 width 中。 - 下一行是什麼?
- 當下一行沒有比當前行縮排更多或更少時,它就是緊隨其後的行,所以它是:
print "perimeter = ", width + height + width + width(它也可能在當前行執行一個函式,但這將在下一章中討論。)那行程式碼做了什麼? - 首先它列印
perimeter =,然後它列印width + height + width + width。 width + height + width + width能正確地計算周長嗎?- 讓我們看看,矩形的周長是底邊(寬度)加上左邊(高度)加上頂邊(寬度)加上右邊(嗯?)。最後一項應該是右邊的長度,也就是高度。
- 你明白為什麼有時周長被“正確”地計算出來嗎?
- 當寬度和高度相等時,它被正確地計算出來了。
我們將要進行程式碼演練的下一個程式是應該在螢幕上列印 5 個點的程式。但是,這是該程式的輸出
. . . .
這是程式
number = 5
while number > 1:
print ".",
number = number - 1
print
這個程式的演練將更復雜,因為它現在有縮排部分(或控制結構)。讓我們開始吧。
- 要執行的第一行是什麼?
- 檔案的第一行:
number = 5 - 它做了什麼?
- 將數字 5 放入變數 number 中。
- 下一行是什麼?
- 下一行是:
while number > 1: - 它做了什麼?
- 嗯,
while語句通常會檢視它們的表示式,如果它是真值,它們就會執行接下來的縮排程式碼塊,否則它們就會跳過接下來的縮排程式碼塊。 - 所以它現在做了什麼?
- 如果
number > 1是真值,那麼接下來的兩行將被執行。 - 那麼
number > 1嗎? - 最後放入
number的值是5,而5 > 1,所以是真值。 - 那麼下一行是什麼?
- 由於
while是真值,所以下一行是:print ".", - 那行程式碼做了什麼?
- 列印一個點,並且由於語句以 ',' 結尾,所以下一個 print 語句將不會出現在不同的螢幕行上。
- 下一行是什麼?
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 ".", - 它做了什麼?
- 它在該行上列印第二個點。
- 下一行是什麼?
- 沒有縮排變化,所以它是:
number = number - 1 - 它做了什麼?
- 它取
number的當前值(4),從中減去 1,得到 3,然後最終將 3 設為number的新值。 - 下一行是什麼?
- 由於 while 迴圈結束導致縮排變化,所以下一行是:
while number > 1: - 它做了什麼?
- 它將
number的當前值(3)與 1 進行比較。3 > 1,所以 while 迴圈繼續執行。 - 下一行是什麼?
- 由於 while 迴圈條件是真值,所以下一行是:
print ".", - 它做了什麼?
- 它在該行上列印第三個點。
- 下一行是什麼?
- 它是:
number = number - 1 - 它做了什麼?
- 它取
number的當前值(3),從中減去 1,並將 2 設為number的新值。 - 下一行是什麼?
- 回到 while 迴圈的開頭:
while number > 1: - 它做了什麼?
- 它將
number的當前值(2)與 1 進行比較。由於2 > 1,所以 while 迴圈繼續執行。 - 下一行是什麼?
- 由於 while 迴圈正在繼續:
print ".", - 它做了什麼?
- 它發現了生命、宇宙和一切的意義。我在開玩笑。(我必須確保你清醒著。)這行程式碼在螢幕上列印第四個點。
- 下一行是什麼?
- 它是:
number = number - 1 - 它做了什麼?
- 取
number的當前值(2),從中減去 1,並將 1 設為number的新值。 - 下一行是什麼?
- 回到 while 迴圈:
while number > 1: - 這行程式碼做了什麼?
- 它將
number的當前值(1)與 1 進行比較。由於1 > 1是假值(1 不大於 1),所以 while 迴圈退出。 - 下一行是什麼?
- 由於 while 迴圈條件是假值,所以下一行是 while 迴圈退出後的行,也就是:
print - 那行程式碼做了什麼?
- 使螢幕進入下一行。
- 為什麼程式沒有列印 5 個點?
- 迴圈過早退出了一個點。
- 我們如何解決這個問題?
- 讓迴圈晚退出一個點。
- 我們如何做到這一點?
- 有幾種方法。一種方法是將 while 迴圈更改為:
while number > 0:另一種方法是將條件更改為:number >= 1還有其他幾種方法。
你需要弄清楚程式正在做什麼。你需要弄清楚程式應該做什麼。弄清楚兩者之間的區別。除錯是一項需要練習才能掌握的技能。如果你在一個小時後仍然無法弄清楚,那就休息一下,和別人談談這個問題,或者思考一下肚臍裡的絨毛。過一會兒再回來,你可能會對這個問題有新的想法。祝你好運。