【发布时间】:2011-05-11 10:58:45
【问题描述】:
在 Python 中是否有某种方法可以捕获 KeyboardInterrupt 事件,而无需将所有代码放入 try-except 语句中?
如果用户按下 Ctrl+C,我想干净利落地退出。
【问题讨论】:
标签: python try-catch keyboardinterrupt
在 Python 中是否有某种方法可以捕获 KeyboardInterrupt 事件,而无需将所有代码放入 try-except 语句中?
如果用户按下 Ctrl+C,我想干净利落地退出。
【问题讨论】:
标签: python try-catch keyboardinterrupt
是的,您可以使用模块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()
【讨论】:
while True: continue。 (无论如何,在那种风格中,while True: pass 会更整洁。)那会非常浪费;试试while True: time.sleep(60 * 60 * 24) 之类的东西(一次睡一天完全是随意的数字)。
time(你应该这样做),不要忘记 import time :)
如果您只想不显示回溯,请将您的代码设为:
## all your app logic here
def main():
## whatever your app does.
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
# do nothing here
pass
(是的,我知道这并不能直接回答问题,但不清楚为什么需要 try/except 块是令人反感的——也许这会让 OP 不那么烦人)
【讨论】:
signal.signal( signal.SIGINT, lambda s, f : sys.exit(0)) 总是这样。
设置自己的信号处理程序的另一种方法是使用上下文管理器来捕获异常并忽略它:
>>> 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 操作的上下文中进行退出后清理,因此将所有内容包装在 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
您可以通过替换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
【讨论】: