Python 3.6。按键 -> 程序执行期间有序退出

Python 3.6. keypress -> orderly exit during program execution

一个我经常看到的问题,但还没有找到适合我问题的解决方案。 因此,如果能帮助我改进我的代码和我对 Python.

的理解,我将不胜感激

我正在处理一大块代码,这些代码具有语法演变的特点,并在多个嵌套(目前不可避免)中运行 classes.

基础class(遗传)处理种群,种群的每个成员都是个体class的一个实例,并使用算法class进行评估。为了避免过于复杂 - 代码在多个层中运行(将 Spice 模拟添加到混合中)并运行......相当长的时间 - 有时数小时。

这就是我开始寻找一种解决方案的原因,该解决方案可以让我以一种不会弄乱一切的方式停止执行。如果我使用通常的 ctrl+c 选项,它只会破坏所有内容并迫使我重新启动内核并丢失所有数据。

我正在考虑的是在代码中添加一种监视器,允许我输入按键序列,然后说 "Ok, I see you want to finish, I'll simply complete the current evaluation loop and exit"。

我认为我不能使用键盘中断,因为这会立即退出代码执行。如果检测到密钥,我更有可能需要一个会改变的标志....

如有任何帮助,我们将不胜感激。

所以总结一下我的伪想法是这样的:

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

谢谢!

虽然可以检测到按键,但您不必这样做。因为可以 "capture" a Ctrl-c!

try:
    code_execution()
except KeyboardInterrupt:
    print("Ctrl-c was pressed, ready to exit")

基本上,当按下 Ctrl-c 时会引发异常类型 KeyboardInterrupt。这是你可以用任何你认为合适的方式捕捉和处理的东西。您可以阅读更多关于此异常的信息,特别是 here and in case you're new to exception handling, this 是一个很好的起点。

奖励点 - 罕见的异常比 if 语句更快。

根据您的评论,我了解到您无法包装主执行并阻止其停止,因此我包含了另一种方法。信号捕获。
Ctrl-c 将我们可以配置的进程 SIGINT 发送到 "trap" 并以不同于正常(正在退出)的方式处理它。

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!")

您可以在他们的 docs 中的 Python 中阅读更多关于信号的信息。它也是进程间通信的标准方法,您可以利用它。

找到使用 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,我们将跳过下一代的评估并退出。对此进行了测试,即使使用外部模拟器也能正常工作!