Python在Windows关机时如何保存集合到文件?

1 投票
2 回答
2823 浏览
提问于 2025-04-18 15:48

我不想在Windows要关机、重启、注销或睡眠的时候丢失我的设置。有没有办法在关机之前保存这些设置?或者有没有其他方法可以保存信息,让我不用担心在Windows关机时会丢失?比如说用JSON、CSV、数据库之类的?有什么建议吗?

s = {1,2,3,4}

with open("s.pick","wb") as f: # pickle it to file when PC about to shutdown to save information
    pickle.dump(s,f)

2 个回答

4

你可以通过 win32api.setConsoleCtrlHandler 来检测 Windows 的关机或注销操作。

这里有一个不错的例子,讲解了如何用 Python 捕捉这些“杀死”事件,链接在这里:如何捕捉“杀死”事件

5

我不想在Windows关机、重启、注销或睡眠时丢失我的设置,能在关机前保存它们吗?

可以的,如果你写了一个有消息循环的应用程序,你可以接收到一个叫做 WM_QUERYENDSESSION 的消息。如果你想要一个图形界面(GUI),大多数图形库可能会以自己的方式处理这个消息。如果你不需要图形界面,最简单的办法可能是使用 PyWin32。在文档中有一个关于创建隐藏窗口和写简单消息循环的教程。只需在主线程中执行这个,然后在后台线程中进行实际工作,当收到 WM_QUERYENDSESSION 消息时,通知你的后台线程。

或者,更简单的是,正如Evgeny Prokurat所建议的,直接使用 SetConsoleCtrlHandler(同样通过PyWin32)。这个方法可以捕捉到 ^C、^BREAK 和用户关闭控制台的情况,以及 WM_QUERYENDSESSION 捕捉到的注销和关机消息。更重要的是,它不需要消息循环,所以如果你没有其他需要消息循环的地方,这样做简单得多。


有没有其他方法可以保存信息,而不必担心在Windows关机时会丢失?比如JSON、CSV、数据库?有什么建议吗?

文件格式并不会神奇地解决问题。不过,使用数据库可能有两个好处。

首先,你可以通过尽可能频繁地写入来减少问题。但对于大多数文件格式来说,这意味着需要尽可能频繁地重写整个文件,这会非常慢。解决方案是将数据流式写入一个更简单的“日志”文件,较少地将其打包到真实文件中,并在每次启动时检查是否有剩余的日志。你可以手动做到这一点,但数据库通常会自动为你处理。

其次,如果在写入过程中程序被杀掉,你可能会得到一个半成品的文件。你可以通过原子写入的技巧来解决这个问题——先写一个临时文件,然后用临时文件替换旧文件,但在Windows上做到这一点比较困难(尤其是使用Python 2.x)(参见 如何正确进行原子写入),而数据库通常会为你处理这个问题。


正确的做法是创建一个新的窗口类,并在 WM_QUERYENDSESSION 消息到达时调用你的处理程序。就像MFC让这比原始的Win32 API代码更简单一样, win32ui(它封装了MFC)让这比 win32api/win32gui(它们封装了原始Win32 API)更简单。你可以找到很多相关的示例(例如,快速搜索“pywin32 msgproc example”会找到像 这个 的例子,搜索“python win32ui”等类似的词也能找到很多)。

不过,在这种情况下,你并不想让窗口像普通窗口那样工作,所以直接去底层写一个简单的消息循环可能更容易。不幸的是,找到这方面的示例代码比较困难——你基本上需要搜索C语言的原生API示例代码(比如在MSDN上查看 创建消息循环),然后再根据 pywin32 的文档将其翻译成Python。虽然这并不理想,尤其是如果你不懂C,但也不是特别难。这里有一个示例可以帮助你入门:

def msgloop():
    while True:
        msg = win32gui.GetMessage(None, 0, 0)
        if msg and msg.message == win32con.WM_QUERYENDSESSION:
            handle_shutdown()
        win32api.TranslateMessage(msg)
        win32api.DispatchMessage(msg)
        if msg and msg.message == win32con.WM_QUIT:
            return msg.wparam

worker = threading.Thread(real_program)
worker.start()
exitcode = msgloop()
worker.join()
sys.exit(exitcode)

我没有展示“如何创建一个最小的隐藏窗口”部分,或者如何用例如 threading.Condition 来通知工作线程停止,因为这些部分有很多更容易找到的好示例;这部分是比较难找的。

撰写回答