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 中程序输出是这样:
这个输出有些丑陋,但将输出复制到 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 中运行了一下,运行过程是这样的:
按下 stop 按钮后的结果:
很奇怪,为什么在 PyCharm 中的输出是这样的效果?
(完)

浙公网安备 33010602011771号