Python 异常处理 -- Return 行,继续

Python Exception Handling -- Return to Line, Continue

我做了很多 Project Euler 编码问题,Python 是我的首选语言。许多程序通常需要永远才能完成,所以我正在努力实现一些有助于提供有关程序状态的诊断信息的东西:当 KeyboardInterrupt 出现时,我希望能够打印程序的运行时间运行 和一些信息可以帮助我确定可能还需要多长时间。

这个问题是——当你点击 Ctrl-C 时捕获 KeyboardInterrupt 对我来说仍然退出程序......我认为这与结构有很大关系这段代码,或者希望 Python 中我还没有找到的东西。

我希望我的代码在 KeyboardInterrupt 被捕获后立即在同一行恢复。

下面是此代码的示例:

     try:
         ...
         ...
         ... #I interrupt code here and hopefully keep going!
         ...
         ...
     except KeyboardInterrupt:
         ...
     finally:
         ...

我希望有人理解这样做的目的,并且可以帮助我找到一种方法来做到这一点,或者解决这种从 运行 代码中调用中断的丑陋方法。

一般来说,在 try 块中触发异常后,您永远不能 return 执行特定代码段,因为异常可能发生在某个地方的深处,并且有许多其他状态受到其他状态的影响其他线程中的副作用。虽然您的程序可能不是这种情况,但鉴于在 Python 中没有允许这样做的通用解决方案,您想要做的事情基本上不可能使用异常处理。

然而,SIGINT 信号的默认处理程序是抛出 KeyboardInterrupt 异常 - 如果您可以劫持它来做其他事情,您实际上可以实现这一点。这是一个简单的程序:

import signal
import pdb

def signal_handler(signal, frame):
    pdb.set_trace()

signal.signal(signal.SIGINT, signal_handler)

count = 0
while True:
    count += 1

SIGINT 处理程序现在只是一个在当前帧调用调试器的函数,每当按下 Ctrl-C 时,调试器就会在代码所在帧内的确切位置触发在。您当然可以像这样简单地检查值:

$ python /tmp/script.py 
^C--Return--
> /tmp/script.py(5)signal_handler()->None
-> pdb.set_trace()
(Pdb) u
> /tmp/script.py(10)<module>()
-> while True:
(Pdb) pp count
13321869
(Pdb) c
^C--Return--
> /tmp/script.py(5)signal_handler()->None
-> pdb.set_trace()
(Pdb) quit
Traceback (most recent call last):
...
    if self.quitting: raise BdbQuit
bdb.BdbQuit

所以调试器在 ctrl-c 完成后被触发,然后我将 up 步进到循环 运行 的帧(在你的情况下,你的代码),然后 c继续执行,再次杀死它并触发了一个终止程序的错误退出(通过键入 quit)。如果你合并它,你基本上可以随时中断并在任何地方检查你的程序的值。

“Yeah,” said Harry. “In . . . in a minute. I’ll just clear this up.” He indicated the smashed bowl on the floor. Ron nodded and left. “ Reparo, ” Harry muttered, pointing his wand at the broken pieces They flew back together, good as new, but there was no re- turning the murtlap essence to the bowl. - Harry Potter and the order of the Pheonix

一旦引发 keyboard interrupt,该信息就会丢失 - try/catch 允许程序恢复,但在此之前发生的事情将永远丢失 [1]

Luckily, the keyboard-interrupt is actually a signal at the c-level - you can intercept and handle it separately, if you'd like to. The traceback and introspect 模块应该有用

对于简单的 hack,这应该没问题,尽管我一般会谨慎使用它,基于信号文档中的 this comment(取决于实现):

We still have the problem that in some implementations signals generated by the keyboard (e.g. SIGINT) are delivered to all threads (e.g. SGI), while in others (e.g. Solaris) such signals are delivered to one random thread (an intermediate possibility would be to deliver it to the main thread -- POSIX?). For now, we have a working implementation that works in all three cases -- the handler ignores signals if getpid() isn't the same as in the main thread. XXX This is a hack.


脚注

[1] 好吧,不,不是。你可以用够了introspection to do anything in python, but it's a terrible idea and doesn't fit with my quote. These people have done it for you if you really want to do something terrible: https://github.com/ajalt/fuckitpy