Python实战教学——第三方库inputimeout源码层面解析
hello大家好,这周想到了一个非常有意思的选题,来给大家分享一下。如题:我想要求用户输入一个字符串,但是又不想让用户无限期地“拖”下去。也就是说,我想做一个有“超时处理”的input函数,这时候我该怎么办呢?我们打开cmd,输入pip install inputimeout,等待下载完成后,在你的代码中输入这么一行:from inputimeout import inputimeout as input
然后你代码中的所有input就支持timeout形式参数了,如果用户没有在指定的时间内输入任何字符,将会报出TimeoutOccurred错误。(timeout的默认值是30秒,无法设置无超时。实际应当重新命名inputimeout,此处图方便)
好了那么本篇文章就此结束……就怪了,你什么时候见过我的文章这么短过?那肯定还有东西啊我们打开控制台窗口,导入这个库之后,输入help(inputimeout)我们把file里面这个地址复制一下,把最后面的文件名删掉,我们就来到了这个库文件的地址。熟悉我的人应该就知道我要干啥了:看源代码啊核心实现是在inputimeout.py中,我们直接看这个文件就可以了。我们来看这个函数:专门处理windows中带有时间限制的输入。借这个机会,我也来教教大家,怎么正确快速地读源代码我建议把代码拷贝到另一个文件中再看代码(防止看的时候乱改还原不了)。注意到里面有很多的名称全部为大写字母的变量,比如“SP”,“CR”这种的,这种变量几乎可以确定是常量。我们又觉得CR这种变量名看不懂是什么,那干脆直接替换成具体的值得了。我们找到这几个变量的赋值的地方,然后直接用它们值的字面量代替变量名。这里要解释一下:\r代表将当前光标回到行开头,\n代表“回车”,即开始新的一行。这个知识点后面会用到代码中还引用了一个echo函数,我们去看一下echo的代码:这个函数不长,而且命名也比较易于理解,所以直接粘贴到我们的文件中好了,现在就完成基础处理了。第一步,先看注释或者函数文档。这个库的作者也是真的不考虑读者感受,注释一行都没有。那我们就自己来补一下注释,边补边看代码。第8行,用到了time库中一个我们没见过的函数:monotonic,我们用help类看一下这个函数翻译一下最后一句话,就是“单调时钟,不能倒退”。我们就可以猜到,它应该是类似time函数一样,提供一个时间获取的渠道。事实上确实是这样,只不过这个函数返回的是cpu时钟,更加精确。第十二行的while循环,意思就是当当前的时间还没有到结束时间时执行,然后开头调用了msvcrt的kbhit函数。我们再用help类来看一下kbhit的帮助文档翻译一下,就是这个函数返回一个非0值如果有按键(输入)等待被读取,否则返回0在Python中,大部分非0非空值在被隐式转换为布尔值时都会转换为True,而0会被转换为False,也就是第十三行这个if就是在判断有没有输入等待被读取,如果有的话继续执行。输入等待被读取说明什么?说明用户输入了内容啊。所以推断这个if内部应该就是处理输入的了。第十四行,msvcrt中的getwche函数我们也没怎么见过。大家也应该会用help类查函数用处了,就不浪费大家时间直接说了:这个函数从标准输入中有回显地读取一个字符,特殊的,ctrl+c和退格、回车也会被读取。第二行,生成了一个cover字符串。它的长度是提示词的长度+当前行内容长度+1,因为上一行将line的最后一个字符删了,所以本质上其实是提示词的长度+原本行内容的长度生成这个字符串有什么用?其实很多终端都不支持真正的“删除字符”功能,我们所做的删除字符只是用空格把这个字符覆盖掉了而已。这个cover就是用于“擦除”这一行中的所有内容生成的临时字符串,这个字符串的所有字符都是空格接下来,调用了输出函数,输出了一个看起来非常复杂的表达式。我们还看到了一个我们没学过的方法:join。这个方法太复杂了,暂时不说。但是在这里,对于 空字符串.join([item1, item2, ...]) ,可以等价于 str(item1)+str(item2)+str(...)。我们来看join中的这个列表。第一个元素是\r,将光标移动至行开头,第二个元素是cover,“擦”掉这行所有的内容。第三个元素又是\r,光标重新回到行开头,第四个元素和第五个元素是prompt和line,重新输出按下退格键后这一行应该有的内容。写到这里的时候,我是有点感慨的:退格键这么一个小功能,我在公众号平台上写这篇文章,用了不下三十次退格键(刚刚又用了三次)。谁都不会认为这个功能实现有多困难,但是它底层的实现原理就是那么复杂。所以当下次用各种计算机中功能的时候(包括鼠标,键盘,甚至是扬声器),请花1秒时间,感谢一下当初那些在计算机刚刚发明时,甚至连汇编都没有的时候,一个一个二进制位输入的程序员们吧!好了,这个函数还有一小段,让我们把它看完吧。if c=="\003"其实就是处理用户按下“ctrl c”停止程序运行的时候,这时候就应当抛出KeyboardInterrupt来结束程序的运行,if c in ("\r","\n")判断用户是否按下了回车键,如果按下了,返回行内容,如果用户一直没有按下的话,那while运行完了后就会抛出错误:用户输入超时。else语段负责将用户输入的字符追加到行末。那么这个函数的工作流程你大概就理解了,这篇文章也快结束了,布置一下课后作业:以下四个任务,从1到4难度逐渐增大(但其实都不大),大家可以自己完成1,修改代码,使得超时后自动返回用户已输入的字符串(即使用户未按回车)。而不是抛出错误2,小明不太明白这段代码while循环中time.sleep的用处,为他解释一下吧(这不是语文题:))3,很多类似的库在用户没有规定时间内按下回车时,都不会返回现在的内容,而是抛出错误,这是为什么?从Python设计哲学和程序员体验角度分析一下4,请为开源程序员们送上一束电子鲜花❀。正因为他们,才有了我们今天如此方便的功能实现