今天,我必须检查我的脚本如何在Windows命令提示符[1]上运行,这时我发现了一些奇怪的东西。我在做类似的事情,但这足以证明这个问题。这是密码。
def bing():
try:
raw_input()
except KeyboardInterrupt:
print 'This is what actually happened here!'
try: # pardon me for those weird strings
bing() # as it's consistent with everything in the chat room (see below)
print 'Yoo hoo...'
except KeyboardInterrupt:
print 'Nothing happens here too!'
情况就是这样。当脚本运行时,它会等待输入,并且用户应该按ctrl+c来引发一个
KeyboardInterrupt
,它(应该)会被except
中的bing()
块捕获。所以,这应该是实际的输出。当我在我的Ubuntu终端上运行它并且空闲时(在windows和Ubuntu上),就会发生这种情况。This is what actually happened here!
Yoo hoo...
但是,在Windows命令提示下,这并不像预期的那样。我宁愿得到一个奇怪的输出。
This is what actually happened here! Nothing happens here too!
它看起来像一个单独的
KeyboardInterrupt
在整个程序中传播,并最终终止它。我尽力了。首先,我使用了一个
signal.signal
来处理SIGINT
(它不起作用),然后我使用了handling函数来生成一个Exception
,稍后我将捕获它(它也不起作用),然后事情变得比以前更复杂了。所以,我回到了我的好老家。然后,我去了蟒蛇馆。@poke suggested当我们按下ctrl+c时,会出现一个
try... catch
符号。然后,当我们按下ctrl+z并回车时,会出现一个@ZeroPiraeus said符号。这很有帮助,推动了discussion after a few minutes of fiddling around。很快,一切都变得混乱了!一些结果是好的,一些是出乎意料的,还有一些went haywire!
结论是停止使用Windows并要求我的朋友使用终端(我同意)。但是,我可以通过捕获
EOFError
和EOFError
来进行变通。虽然每次按ctrl+z键并输入都很懒惰,但这对我来说并不是什么大问题。但是,这对我来说是一种困扰。在进一步的研究中,我还注意到当我按下ctrl+c时,命令上没有出现任何提示。
底部什么也没有。那么,这里到底发生了什么?为什么
EOFError
会传播?有没有办法使输出与终端一致?[1]:我一直在终端上工作,但今天我需要确保我的脚本在所有平台上都能工作(特别是因为我的大多数朋友都是非编码人员,只需坚持使用Windows)。
最佳答案
链接的问题以某种方式解释了这一点:user2357112。
键盘中断是异步引发的,因此它不会立即终止应用程序。相反,ctrl+c是在某种需要一段时间才能到达的事件循环中处理的。不幸的是,在这种情况下,您无法可靠地捕获KeyboardInterrupt
。但是我们可以做一些事情来到达那里。
正如我昨天解释的,停止raw_input
调用的异常不是KeyboardInterrupt
而是EOFError
。您可以通过如下方式更改bing
函数来轻松验证这一点:
def bing():
try:
raw_input()
except Exception as e:
print(type(e))
您将看到打印的异常类型是
EOFError
而不是KeyboardInterrupt
。您还将看到print
甚至没有完全通过:没有新行。这显然是因为输出被刚好在print语句将异常类型写入stdout之后到达的中断中断了。当您在打印中添加更多内容时,您也可以看到这一点:def bing():
try:
raw_input()
except EOFError as e:
print 'Exception raised:', 'EOF Error'
注意,我在这里为print语句使用了两个独立的参数。执行此操作时,我们可以看到“异常引发”文本,但不会显示“EOF错误”。相反,来自外部调用的
except
将触发并捕获键盘中断。但在python 3中,情况变得更加失控。采用此代码:
def bing():
try:
input()
except Exception as e:
print('Exception raised:', type(e))
try:
bing()
print('After bing')
except KeyboardInterrupt:
print('Final KeyboardInterrupt')
这与我们之前所做的差不多,只是针对python 3语法进行了修改。如果我运行这个,我会得到以下输出:
Exception raised: <class 'EOFError'>
After bing
Final KeyboardInterrupt
因此,我们可以再次看到,eoferror被正确捕获,但是由于某种原因,这里的python 3继续执行的时间比python 2长得多,因为执行了
bing()
之后的打印。更糟糕的是,在使用cmd.exe执行的某些操作中,我得到的结果是根本没有捕获到键盘中断(很明显,中断是在程序完成后处理的)。那么,如果我们想确保得到一个键盘中断,我们该怎么做呢?我们可以肯定的是,中断一个提示总是会引发一个我们一直看到的一致的提示。所以我们能做的就是抓住它,然后确保我们得到键盘中断。
这样做的一种方法是只从
input()
的异常处理程序中引发一个raw_input()
。但这不仅感觉有点脏,也不能保证中断实际上是在第一时间终止输入提示的原因(谁知道还有什么可能引发eoferror?)。所以我们应该让已经存在的中断信号生成异常。我们这样做的方法很简单:我们等待。到目前为止,我们的问题是,由于异常没有足够快地到达,所以执行继续进行。那么,如果我们稍等一点,让异常最终到达,然后再继续处理其他事情呢?
import time
def bing():
try:
input() # or raw_input() for Python 2
except EOFError:
time.sleep(1)
try:
bing()
print('After bing')
except KeyboardInterrupt:
print('Final KeyboardInterrupt')
现在,我们只需要捕获eoferror并稍等,让后面的异步进程稳定下来,然后决定是否中断执行。这始终允许我捕获外部try/catch中的
EOFError
,除在异常处理程序中执行的操作外,不会打印其他任何内容。您可能会担心,等待一秒钟的时间很长,但在我们的情况下,在我们中断执行的情况下,这一秒永远不会持续太长。就在
KeyboardInterrupt
之后几毫秒,中断被捕获,我们进入了异常处理程序。因此,这一秒只是一个故障保险箱,它将等待足够长的时间,异常肯定会及时到达。在最坏的情况下,当没有一个真正的中断,只是一个“正常”的eoferror?那么,以前为用户输入而无限阻塞的程序将需要一秒钟的时间才能继续;这不应该是一个真正的问题(更不用说Eofiror可能是非常罕见的)。所以我们有了解决办法:抓住电铁氧体,等一下。至少我希望这是一个可以在其他机器上工作的解决方案,而不是我自己的^ ^“昨晚之后,我不太确定这一点,但至少我在所有终端和不同的python版本上获得了一致的经验。