Python 開源代碼分析 01 | 一個簡單的計時器小工具

近來感覺自己編碼動手能力實在太差,平時又沒有什麼編程的點子,但又不能學而不練,於是想到要不自己來解讀一些開源代碼並把分析過程編輯成博客吧。遂有了這篇博客。這將會是一系列的源碼解讀博客,這是該系列的第一篇,源碼來源一般爲 Github 等網站。

關於閱讀代碼這回事,個人覺得和閱讀文章和書籍十分相像。我認爲一位好的作家肯定是一位好的讀者,如果想要讓自己寫出精彩的文字,那麼肯定少不了大量地閱讀其他人寫下來的精彩的文字。這個道理放在編程中,我覺得也一樣。

本人在編程方面還是一個新手,這也是我頭一次這麼正經地分析代碼,分析代碼能力也處於新手水平。雖然這次分析的代碼很簡單,但如果大家發現任何問題的話,還請指出。

開始分析代碼吧。

代碼

代碼來源 - 點擊查看源碼

這個 repo 裏有許多作者寫的用來解決自己日常問題的 Python 小工具,在這裏也向大家推薦這個 repo

我故意挑了一個這個 repo 中的一個簡單的小工具,一個簡單的計時器。

雖然程序很簡單,但用到的 Python 語法工具卻還挺多,比如 time 內置模塊、異常處理等。

# Author: OMKAR PATHAK
# This script helps to build a simple stopwatch application using Python's time module.

import time

print('Press ENTER to begin, Press Ctrl + C to stop')
while True:
    try:
        input()  # For ENTER. Use raw_input() if you are running python 2.x instead of input()
        starttime = time.time()
        print('Started')
        while True:
            print('Time Elapsed: ', round(time.time() - starttime, 0), 'secs', end="\r")
            time.sleep(1) # 1 second delay
    except KeyboardInterrupt:
        print('Stopped')
        endtime = time.time()
        print('Total Time:', round(endtime - starttime, 2), 'secs')
        break

代碼分析

這個程序導入了一個 time 模塊。

程序運行會先打印一條語句:Press ENTER to begin, Press Ctrl + C to stop

然後進入一個 while 無限循環。

在第一個 while 循環內部使用了一組 try-except 語句塊,我們來看看。不過在此之前,對於這麼一個無限的 while 循環,會很自然地產生了一個問題:這個循環的終止條件是什麼?一般來說,想要退出這個循環,需要使用 break語句,所以我們找一找代碼中有沒有 break 語句。果然,break 語句在代碼的最後一行。這也就意味着,當程序出現一個稱爲 KeyboardInterrupt 異常時,最外層的 while 循環(第7行)就會終止。

接着來看 try 語句塊內部。input() 函數大家都不陌生,這裏的 input() 函數的作用其實是等待一個回車鍵輸入(我很好奇,input() 函數收到一個回車鍵輸入後,底層會發生什麼事,但這次先不討論這個問題)。收到一個回車鍵輸入後,這個計時小程序開始計時。

作者聲明瞭一個 starttime 變量。在這裏首先得解釋一下 time 模塊中 time() 函數。time() 會返回當前時間的時間戳。什麼是時間戳

時間戳是指格林威治時間自1970年1月1日(00:00:00 GMT)至當前時間的總秒數 [來源]

ok,也就是說調用 time() 函數我們會得到1970年1月1日0時0分到我們當前時間的總秒數,比如像下面這樣:

>>> import time
>>> time.time() # current time: 2020-04-11
1586593438.927243

所以,starttime 變量會的值爲程序運行到這條語句時的時間戳。

然後是第 11 行,打印一句話,沒什麼好說的。

第 12 行,又進入一個無限的 while 循環。循環體中,會先打印一條語句,然後調用 time 模塊的 sleep() 函數,無限重複這個過程。sleep() 的功能很簡單,讓系統休眠指定秒數,在程序中則是讓系統休眠 1 秒。主要是看第 13 行語句,也就是打印輸出的這條語句。在語句中用到了 Python 內置的 round() 函數,對於這個函數,其實輸出結果理解起來很簡單:輸入一個數值(一般爲浮點數),返回這個數值的四捨五入的結果,如:

>>> round(1.2)
1
>>> round(1.5)
2
>>> round(1)
1

round() 函數還可以接收另一個參數,用這個參數可以指定保留小數點後幾位,示例如下:

>>> round(1.22222, 2)
1.22
>>> round(1.22666, 3)
1.227

但在某些情況下這個函數的輸出結果並不符合四捨五入這個法則,對於這個問題,在這裏就不討論了,感興趣的朋友可以參考這些資料:round 函數用法關於 round 函數的小坑

繼續分析程序,print() 語句中使用了 round() 函數作爲參數,我們來看看 round() 函數的其中一個參數:

time.time() - starttime

這個參數的結果就是秒數,當前時間減去計時開始時間。

try 語句塊分析完了,下面來看 except 語句塊。

except 塊接到 KeyboardInterrupt 後,會打印一條語句,Stopped。然後聲明一個 endtime 變量,將程序出現異常時的時間戳賦給該變量。然後打印一條語句,又用到了一個 round() 函數,第一個參數中,endtime 減去 starttime,結果就是計時器所記錄的總秒數。round()的第二個參數表示結果保留 2 位小數。

最後,調用break語句,結束最外層的while循環(第7行)循環,程序結束。

程序分析完了,來看看程序運行的結果吧。

源碼思路分析

分析一下這個程序的實現思路。

程序的功能就是個簡單的控制檯計時器。個人認爲最外層的 while 循環可以不加。不太理解爲什麼作者要在最外面也加一個 while 循環。

計時開始。第 9 行 input() 函數的作用好比是計時器的開始按鈕(總不能剛一運行程序就開始計時吧)。按下按鈕後,記錄一個開始時間,然後開始計時。計時的功能通過 while 循環加上 time 模塊的 sleep() 來實現(第12 - 14行)。邊計時邊打印已經過了幾秒。

那麼如何停止計時呢?按下 ctrl + c,系統會拋出一個 KeyboardInterrupt 異常,計時隨之停止。作者編寫了一個 except 語句塊來處理這個異常,最終輸出總秒數。

運行程序

這一小節記錄了程序在我的計算機中運行的結果,以及我產生的一些疑問。

在我的計算機的(win10,64位操作系統)IDLE 中程序輸出是這樣:

image-20200411155841826

這個輸出有些醜陋,但將輸出複製到 markdown 文檔裏後,會變成下面這樣,目前不清楚是什麼原因:

Press ENTER to begin, Press Ctrl + C to stop

Started
Time Elapsed:  0.0 secs
Time Elapsed:  1.0 secs
Time Elapsed:  2.0 secs
Time Elapsed:  3.0 secs
Time Elapsed:  4.0 secs
Time Elapsed:  5.0 secs
Stopped
Total Time: 6.26 secs

這個小程序我在 PyCharm 中運行了一下,運行過程是這樣的:

image-20200411155841826
image-20200411155841826

按下 stop 按鈕後的結果:

image-20200411155841826

很奇怪,爲什麼在 PyCharm 中的輸出是這樣的效果?

(完)

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