跳轉到內容

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 = ,然後它列印變數 widthheight 中包含的值的總和,從 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 還有其他一些方法。

如何修復我的程式?

[編輯 | 編輯原始碼]

你需要弄清楚程式在做什麼。你需要弄清楚程式應該做什麼。找出兩者之間的差異。除錯是一項需要練習才能掌握的技能。如果你在一個小時後仍然無法解決問題,那就休息一下,和別人談談這個問題,或者沉思你肚臍裡的絨毛。過一會兒再回來,你可能會對這個問題有新的想法。祝你好運。

Python 3 非程式設計師教程
 ← 決策 除錯 定義函式 → 
華夏公益教科書