有没有一种方法可以在 Python 中捕获 KeyboardInterrupt
事件,而不必把所有代码放在 try
-except
语句中?
如果用户按下 Ctrl+C,我想要干净地退出而不留下任何痕迹。
有没有一种方法可以在 Python 中捕获 KeyboardInterrupt
事件,而不必把所有代码放在 try
-except
语句中?
如果用户按下 Ctrl+C,我想要干净地退出而不留下任何痕迹。
是的,您可以使用模块 signal 安装中断处理程序,并使用 threading.Event 永久等待:
import signal
import sys
import time
import threading
def signal_handler(signal, frame):
print('You pressed Ctrl+C!')
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
print('Press Ctrl+C')
forever = threading.Event()
forever.wait()
如果你只是想不显示 traceback,将你的代码改成这样:
## all your app logic here
def main():
## whatever your app does.
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
# do nothing here
pass
(是的,我知道这并没有直接回答问题,但不太清楚为什么需要一个try/except块会引起反对 - 也许这可以减轻提问者的烦恼)
signal.signal(signal.SIGINT, lambda s, f: sys.exit(0))
总是有效的。 - Hal Canary除了设置自己的信号处理程序外,另一种方法是使用上下文管理器来捕获并忽略异常:
>>> class CleanExit(object):
... def __enter__(self):
... return self
... def __exit__(self, exc_type, exc_value, exc_tb):
... if exc_type is KeyboardInterrupt:
... return True
... return exc_type is None
...
>>> with CleanExit():
... input() #just to test it
...
>>>
这将去除 try
-except
代码块,同时保留一些明确说明正在进行的操作。
这还允许您在代码的某些部分中忽略中断,而无需每次设置和重置信号处理程序。
我知道这是一个老问题,但我先来到这里,然后发现了 atexit
模块。目前我还不知道它的跨平台记录或所有注意事项的完整列表,但到目前为止,正是我在尝试处理Linux上 post-KeyboardInterrupt
清理时所寻找的东西。只是想再提供另一种解决问题的方式。
我想在 Fabric 操作的上下文中进行 post-exit 清理,因此用 try
/except
将所有东西都包装起来对我也不是一个选项。我觉得在这种情况下,atexit
可能是一个很好的选择,因为你的代码不处于控制流的最高级别。
atexit
在开箱即用时非常强大且可读,例如:
import atexit
def goodbye():
print "You are now leaving the Python sector."
atexit.register(goodbye)
你还可以将其用作装饰器(从2.6版本开始;以下示例摘自文档):
import atexit
@atexit.register
def goodbye():
print "You are now leaving the Python sector."
KeyboardInterrupt
进行设置,那么这个问题的其他回答可能更适合你。但是请注意,atexit
模块仅有大约70行代码,创建一个类似的版本并使异常以参数的形式传递给回调函数是很容易的。(需要修改的atexit
限制: 目前我无法想象如何让退出回调函数知道有关异常的信息; atexit
处理程序捕获异常,调用您的回调函数,然后重新引发该异常。 但是您可以使用不同的方法。) 详见以下链接:
atexit
在 MacOS 和 Ubuntu 18.04 上对于 Python 3.7 和 3.8 都有效。 - Ainz Titor您可以通过替换sys.excepthook
来防止打印KeyboardInterrupt
的堆栈跟踪,而不需要使用try: ... except KeyboardInterrupt: pass
(这是最明显和可能是“最好”的解决方案,但您已经知道它并且要求其他方法)。示例代码如下:
def custom_excepthook(type, value, traceback):
if type is KeyboardInterrupt:
return # do nothing
else:
sys.__excepthook__(type, value, traceback)
我试着使用大家提供的解决方案,但最终只能自己动手修改代码才使其正常工作。以下是我的修改后的代码:
import signal
import sys
import time
def signal_handler(signal, frame):
print('You pressed Ctrl+C!')
print(signal) # Value is 2 for CTRL + C
print(frame) # Where your execution of program is at moment - the Line Number
sys.exit(0)
#Assign Handler Function
signal.signal(signal.SIGINT, signal_handler)
# Simple Time Loop of 5 Seconds
secondsCount = 5
print('Press Ctrl+C in next '+str(secondsCount))
timeLoopRun = True
while timeLoopRun:
time.sleep(1)
if secondsCount < 1:
timeLoopRun = False
print('Closing in '+ str(secondsCount)+ ' seconds')
secondsCount = secondsCount - 1
while True: continue
这样的代码风格,尽管在那种情况下,while True: pass
更加简洁。使用while True: continue
会很浪费资源,推荐使用类似while True: time.sleep(60 * 60 * 24)
的方式(每次睡眠一天是完全随意的)。 - Chris Morgantime
(正如你应该做的那样),别忘了要import time
:) - Seaux