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 中的输出是这样的效果?

(完)

posted @ 2020-04-11 17:01  TheByteMan  阅读(627)  评论(0)    收藏  举报