Python中正确的终止方式

7
我有一堆实例,每个实例都有一个唯一的临时文件用于存储从内存中保存数据以便以后检索。
我希望确保在一天结束时,所有这些文件都被删除。然而,我想留出空间进行精细的控制其删除。也就是说,如果需要,可以提前删除一些文件(例如,它们太大并且不再重要)。
最佳/推荐的方法是什么?
我的想法
  • 由于我们有许多文件,它们的生命周期可能会重叠,因此 try-finalize 块或 with 语句不是一个可选项。此外,它几乎不允许更细粒度的控制。

  • 从我所读的内容来看,__del__ 也不是可行的选择,因为甚至不能保证它最终会运行(尽管对于哪些情况是“有风险”的并不完全清楚)。此外(如果仍然是这种情况),当 __del__ 运行时,库可能不可用。

  • tempfile 看起来很有前途。但是,文件在关闭后就消失了,这肯定是个问题,因为我希望在它们不执行任何操作时关闭它们,以限制打开文件的数量。

    • 该库承诺,文件“将在关闭时立即被销毁(包括对象垃圾收集时的隐式关闭)”。

      他们如何实现隐式关闭?例如在 C# 中,我会使用(可靠的)终结器,而 __del__ 不是终结器。

  • atexit 似乎是最佳选择,它可以作为可靠的终结器代替 __del__ 实现安全的一次性模式。与对象终结器相比,唯一的问题是它真正运行于退出时,这相当不方便(如果对象有资格更早地被垃圾回收,怎么办?)。

    • 这里,问题仍然存在。该库如何确保方法始终运行?(除非发生了真正意外的情况,否则很难做任何事情)
在理想情况下,似乎__del__atexit库的组合可能表现最佳。也就是说,在__del__和在atexit中注册的方法都会进行清理,而重复的清理将被禁止。如果调用了__del__,则已注册的方法将被删除。
唯一(但至关重要)的问题是,如果在atexit中注册了一个方法,__del__将不会运行,因为对对象的引用将永远存在。
因此,任何建议、意见、有用的链接等都是受欢迎的。

你可以把所有最终想要删除的路径放进一个列表里,在某个时候遍历这个列表并删除所有存在的文件,这样怎么样? - khelwood
@khelwood 谢谢您的建议。然而,对我来说这有点不方便。通常情况下,当一个对象释放其支持信息隐藏原则的资源时,会更好一些。例如,如果tempfile被云存储所取代,那么与更改相关的代码就需要重写(更不用说两种类型的对象,一个带有tempfile,另一个带有云存储或更多此类类型的情况)。然而,对这些对象进行常见的“dispose”调用肯定是在菜单上的,尽管我仍然更喜欢自动完成(即由GC完成)。 - Tomáš Hons
1个回答

10

我建议考虑使用内置模块weakref来完成此任务,更具体地说,可以参考weakref.finalize的简单示例:

import weakref
class MyClass:
    pass
def clean_up(*args):
    print('clean_up', args)
my_obj = MyClass()
weakref.finalize(my_obj, clean_up, 'arg1', 'arg2', 'arg3')
del my_obj  # optional

运行时将输出

clean_up ('arg1', 'arg2', 'arg3')

请注意,即使没有删除my_obj(您可能会删除代码的最后一行,但行为不会改变),clean_up也将被执行。 clean_up在所有对my_obj的强引用消失或结束(例如使用atexit模块)后调用。

是的,看起来正是我正在寻找的东西。谢谢! - Tomáš Hons
请确保清理函数及其参数不持有对对象的引用,否则对象将永远不会被垃圾回收。cleanup不能是MyClass的一个方法(您的示例是正确的,但在调整示例以适应自己的代码时很容易搞砸)。要查看该概念的生产版本,请参阅multiprocessing模块如何完成用户未关闭的Manager对象的终止操作:https://github.com/python/cpython/blob/main/Lib/multiprocessing/util.py和https://github.com/python/cpython/blob/main/Lib/multiprocessing/managers.py - julaine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接