Python 初學番外篇—如何測試自己的程式

利用檔案處理、線上開發環境或是 Command-Line 來測試自己的程式

Yu-Hsuan Chou
ccClub
11 min readOct 26, 2019

--

在學習任何程式語言的過程中,都需要學習如何測試自己的程式,以得到正確的結果或是修改自己的程式來完成需求。但有時候只是想要對程式進行測試,卻發現測試資料太過冗長,一行一行輸入並不太方便,因此以下將在給定一個題目的情況下,試著提供幾個測試自己程式時可以利用的小技巧。

舉例來說,如果我們今天有一個題目,敘述如下:

意思就是我們會需要計算兩種不同費用的和,一個是總路程乘上費率1,另一個是東西南北四個方向單次最大移動距離的和乘上費率2,以及經過移動以後的座標位置。

範例測資:

那麼我們解題的思維可能像這樣:

打算測試的程式碼如下:

image created by carbon.now.sh

了解了這次要處理的問題,以及我們試寫的程式碼以後,接下來我們會介紹三種方式來測試這段程式碼,希望可以讓大家學會這些方法以後,更加熟練的測試自己的程式:

  1. 使用任意編輯器搭配檔案處理
  2. 利用線上開發環境執行所寫程式碼
  3. 透過 Command-line 簡化輸入/輸出流程

使用任意 Python 編輯器搭配檔案處理

Python 初學第十二講 — 檔案處理當中,曾經提過當我們的程式要進行檔案處理時的方法。當然,要從檔案裡讀取的,不一定只是我們需要處理的資料,我們也可以將輸入複製貼上在檔案當中,以供程式讀取。

因此我們的測試方法可能會以如下的思路進行:我們先將測資複製貼上到一個文字檔案,並且存檔,然後再利用 file 的方法將這個文字檔逐行讀入,並利用每讀進檔案中的一行內容視為使用一次 input() 的方法來讀進我們的程式當中。

我們將測資複製貼上進文字檔案中,在此處我們選擇 txt 的檔案類型,但其他檔案類型,如 in 等也可以用以下的方法讀取。

而我們利用 file.readline() 取代 input() 的改編如下:

可以依據問題以及使用情況來自由選擇 file.readline() 或是 for line in file 的方法來模擬逐行讀取的動作,舉例來說,若我們需要大量處理相同或類似格式的輸入,通常會選擇後者;但若輸入的行數固定,前者也不失為一個選擇。

使用 Jupyter Notebook 執行上述程式碼的執行結果如下圖:

如果不使用 Jupyter Notebook 而是使用其他編輯器的情況底下,我們的程式碼通常都是儲存成一個 .py 檔案來執行的,如果不想在檔案中將測資檔案的位置寫死,又該怎麼做呢?此時我們要利用的是 Python 的內建模組 sys

sys 是 Python 內建的模組,通常用來取得系統的參數或是函式。而這次我們所要用的,就是 sys 當中的物件 argvargv 用來取得從命令列傳入的參數,藉由這個參數,我們可以在執行 .py 時才給定檔案路徑,不需要一開始在程式碼當中寫死。使用的方法如下:

我們將如下的程式碼,利用任何編輯器(Python原生IDLE、sublime、Visual Studio Code、Spyder、PyCharm等等)儲存在一個 .py 檔中,並命名為 taxi.py

我們在原本放檔案路徑的位置上,放了 sys.argv[1] 這個參數。為什麼是 sys.argv[1] 呢?這就要提到這個程式碼的執行方式了。先來看看怎麼執行這個 .py 檔吧!通常利用命令列(terminal/cmd)執行 Python 程式碼的方法如下:

我們可以利用命令列來執行上述的指令,讓 python 來跑我們的 .py 程式碼。但是今天我們希望這個程式碼得到來自外面的輸入,故寫法如下:

執行結果如下圖:

我們利用 Python3 來叫我們電腦裡的 Python 執行 taxi.py 這個程式碼,然後給定檔案路徑,利用 sys.argv 來將檔案路徑放進程式碼當中讀取檔案的位置。那麼回到剛剛的問題,為什麼是 sys.argv[1] 呢? sys.argv 是一個系統參數的串列,在上圖的範例裡,sys.argv[0] 指的是我們所給的程式碼 taxi.py 的路徑,而 sys.argv[1] 指的就是放在第二個參數,也就是檔案路徑。

至此我們完成了藉由檔案的輸入來測試程式的方法,藉由檔案處理的小技巧,可以模擬使用者的輸入,簡化手動輸入的流程來測試我們的程式,得到我們所要的輸出結果。跟讀取檔案相關的詳細做法可以參考Python 初學第十二講 — 檔案處理的內容。

利用線上開發環境執行所寫程式

當然,有很多事情不需要我們自己親力親為,網路上的線上資源相當多,如果我們沒有已經設定好的程式開發環境,在此我們也推薦一個線上的 IDE (整合開發環境),可以讓我們輕鬆地輸入較為大量的測資,並且執行我們所寫的 py 檔。

Repl.it: 一個支援多種程式語言的雲端開發環境,包含 Python、node.js、C、C++、Java、Go…等等,結合程式設計、專案開發以及教學用途,也是許多人如果一時之間沒有適當開發環境時的首選。介面長相如下:

repl.it 的使用介面

當我們寫完程式以後,只需要按 run 就可以執行,並可以直接利用右側的命令列,直接貼上測資即可。

示範如下:

藍框處即為我們測試的結果輸出,在貼上測資以後按 enter 就會直接跑出結果,呈現在螢幕上。使用相當便捷,但若輸出較為複雜,或是需要另外將輸出儲存起來,就不是那麼適合。

透過 Command-Line 簡化輸入流程

最後要介紹的方法是利用上面提到的,利用命令列(Terminal/cmd)來執行程式碼的方法。剛才我們提到過,如果我們要用命令列執行一個程式碼,寫法如下:

然而,有一個十分簡單的方法可以讓我們將測試資料輸入進我們的程式,不需要將 input() 改成任何東西,只需要使用最原本的 code :

我們將上面的 code 存進 taxi_simple.py 這個程式碼檔案當中,並且使用剛才的 testcase.txt 。接著,我們命令列要輸入的指令如下:

執行結果如下:

由上圖可見,我們使用了一個小於符號 < ,這個符號是用來打開符號後面的檔案(也就是我們的 testcase.txt),並且程式的標準輸入從來自命令列(鍵盤輸入)改成該檔案的內容,讓程式碼讀進去的方法。利用這個簡單的小符號,就可以幫助我們完成複雜的測資輸入流程,是不是很方便呢?

到這裡為止我們分享了幾種不同的輸入測資方式,但有時我們不只需要面對冗長的輸入,還需要處理同樣冗長的輸出,光是比對輸出是否正確都經常需要花上許多時間,因此我們也來分享幾個實用的小技巧,接下來要補充如何得到輸出的結果,並存成檔案,以及如何比對是否正確。

如何儲存測試結果?

當然,我們可以透過檔案處理的輸出方式來儲存我們的測試結果,利用 file.write() 或是 print() 時透過指定參數 file 的方式來寫入檔案,詳細的方法可以參見Python 初學第十二講 — 檔案處理。總而言之,我們結合剛才所提到的 sys.argv ,改寫程式碼如下並儲存為 taxi_out.py 這個檔案:

而此時的用法則如下:

結合程式碼很明顯可以看出,我們將 sys.argv[1] 作為測試輸入的檔案路徑讀進程式碼,進行運算以後,再輸出至 sys.argv[2] 所給的檔案路徑。測試結果如下:

可以看出,我們所跑出的結果都儲存進了 out.txt 這個檔案當中。

那麼,有沒有跟剛才使用 < 一樣簡單的方法呢?當然有,就是使用 > 這個符號。同樣使用最一開始的程式碼 simple_taxi.py

接著使用如下的方法:

相對於剛才 < 的定義,在此使用 > 則是代表我們將原本要輸出在命令列當中的輸出,改成輸出到指定的檔案當中,執行結果就會被儲存在我們所指定的檔案當中。執行結果如下圖:

上述兩種方法都可以將我們的輸出存成檔案,以供後續使用。

如何比對輸出結果?

當輸出結果太過冗長,光是要比對都要花上許多時間和精力。儲存成檔案的好處就是,可以不必用肉眼來比對自己的結果以及正確答案,而是可以透過命令列的指令來完成這個工作,在此分享在不同作業系統(Windows, Mac OS, Linux, Unix)下的解法。

如果使用的是 mac 以及 Linux、Unix,用法如下:

可以直接比對兩個檔案的內容,如果有不同的地方,將會告訴我們在哪個位置有差異。舉例來說,如果我們有兩個檔案內容如下:

兩者的差異只在於第二行的最後一位數字,此時若我們使用 diff 命令,可以得到執行結果如下:

從輸出的結果可以看出這兩個檔案不同之處,若兩個檔案內容一模一樣,將不會輸出任何東西。

至於 Windows 作業系統下的比較,可以使用 FC 這個命令,也可以安裝 WinMerge 等等用於檔案、程式碼比較的軟體,就可以輕鬆的完成比較結果的過程,並且據此修改程式碼,以完成我們的工作或是專案。

學會如何善用命令列或是小工具來完善我們的程式碼,有助於我們進行程式設計的學習以及進步,也可以簡化我們許多不必要的時間成本,朝著成為更好的程式設計者之路邁進。

--

--