一个我经常看到的问题,但还没有找到适合我的问题的解决方案。因此,我希望得到一些帮助,以改善我的代码和我对 Python 的理解。
我正在处理一大块代码,它的特点是语法进化,并在几个嵌套的 (目前不可避免的) 类中运行。
基类(Genetic)处理种群,种群中的每个成员都是 Individual 类的实例,并使用 Algorithm 类进行评估。为了避免过度复杂化--一个代码要运行好几层(加上Spice模拟),而且运行时间......相当长--有些时候要几个小时。
这就是为什么我开始寻找一种解决方案,让我能够以一种不会搞砸一切的方式停止执行。如果我使用通常的ctrl+c选项,它会简单地摧毁一切,迫使我重新启动内核,并丢失所有的数据。
我想的是在代码中添加一种监视器,让我输入一个按键序列,然后说:"好的,我知道你想结束,我将简单地完成当前的评估循环并退出"。
在我看来,我不能使用键盘中断,因为这将立即退出代码执行。更有可能的是,我需要一个标志,如果检测到按键就会改变......。
如果有任何帮助,我将感激不尽。
所以,总结一下我的伪想法是这样的。
for each generation:
if exit.flag != true:
for each individual:
evaluate individual
else:
write result
finish and exit
when key detected set exit.flag to true
谢谢你!
使用 pynput 找到了一个解决方案
from genetic.mainCore import Genetic
from grammar.basicDict import params
from algorithm.core import Algorithm
from pynput import keyboard
def on_activate_h():
print('<ctrl>+<alt>+h pressed')
global gen
gen.forcEnd = True
listener = keyboard.GlobalHotKeys({'<ctrl>+<alt>+h': on_activate_h})
listener.start()
algi = Algorithm()
gen = Genetic(algi)
gen.initGrammar(params)
gen.initPopulation()
gen.run()
listener.stop()
我修改了gen.run()方法,加入了一个forcEnd检查。如果它被设置为True,我们就跳过下一代的评估并退出。经过测试,即使在使用外部模拟器时也能正常工作。
虽然有可能检测到一个按键,但你不必这样做。因为可以 "捕获 "一个Ctrl-c!
try:
code_execution()
except KeyboardInterrupt:
print("Ctrl-c was pressed, ready to exit")
基本上,异常类型 KeyboardInterrupt
当按下Ctrl-c时,会被抬起。你可以用你认为合适的方式来捕捉和处理这个问题。你可以阅读更多关于这个异常的内容,特别是 此处 如果你对异常处理很陌生。这个 是一个很好的起点。
额外的一点--罕见的异常比if语句快。
从你的评论中,我了解到你不能包裹主执行并防止它停止,所以我加入了另一种方法。信号诱捕。Ctrl-c向进程发送SIGINT,我们可以将其配置为 "陷阱",并以不同于正常的方式处理(就是退出)。
import signal, os
import time
FLAG = False
def handler(signum, frame):
"""
This function will execute each time Ctrl-c is pressed or SIGINT
is sent to the process (such as `kill <pid>`).
"""
global FLAG
print('Signal handler called with signal', signum)
FLAG = True
# Setup so we process signal SIGINT (includes Ctrl-C) with handler
signal.signal(signal.SIGINT, handler)
while True and not FLAG:
time.sleep(0.5)
print("Working hard!")
else:
print("Done!")
你可以在Python中阅读更多关于信号的内容,在他们的 文件. 这也是一种标准的进程间通信方法,你可以利用它。