元类单例对象中的析构函数

11

我正在修改一个使用元类实现单例模式的旧库。

继承自type类的Singleton类定义了__call__函数。

目前,使用此库创建的单例对象永远不会被删除。我在单例类中定义了__del__方法,但是这个方法从未被调用。

澄清一下:我已经实现了一个名为Singleton的(元)类,它被多个类使用,并将Singleton用作__metaclass__

例如,我有一个class A(object),它具有__metaclass__ = Singleton。A类有几个成员变量,我希望它们在程序结束时随着A对象(唯一存在的对象)的销毁而被销毁。

我尝试在A类中定义__del__方法,但是它不起作用。


了解为什么需要销毁单例可能会有所帮助。你只有一个。(对吧?你说话的时候好像有多个单例,这很令人困惑。)如果你只有一个,那么销毁它是不寻常的。 - senderle
你是对的。我正在编辑我的问题。 - JoseLSegura
在某些测试场景中,创建一个自我清理的fixture会有益处,而在生产环境中,只需要单个实例的对象是有意义的,并且通常不需要清理。 - Chen Levy
1个回答

9

要点1: __del__()可能在进程退出时不会被调用

首先要说的是,__del__()方法并不能保证在解释器退出时调用仍然存在的对象。

来自Python数据模型文档。因此,您不应该依赖它来清理您需要在退出时整理的状态,而在最高级别上,这就是为什么您的__del__()可能没有被调用的原因。这就是atexit的作用。

要点2: 可预测的对象生命周期是Python中的实现细节

接下来要说的是,虽然CPython使用引用计数使其能够检测到可以释放对象而无需使用垃圾回收器(导致更可预测的CPU影响和更高效的应用程序),但只需要一个循环引用、一个未清除的异常、一个遗忘的闭包或一个不同的Python实现即可破坏它,所以您应该认真考虑是否要依赖于在特定时刻调用__del__()

要点3: 单例实现通常维护单例实例的全局引用

听起来,我猜您的单例元类(本身是一个单例...)在第一次调用__call__()时保留了您的单例实例。由于元类未释放,因为它属于模块,该模块本身被sys.modules保留,因此该引用不会在程序终止时消失,因此即使有对单例的所有外部引用进行了及时清除,__del__()也不会被调用。

您可以尝试的方法

  1. 创建单例实例时,添加atexit处理程序以在进程退出时进行必要的清理。
  2. 如果您想要,在__del__()方法中也可以进行清理。例如,出于整洁/未来可扩展性的考虑(例如将单例变成复数形式),您可能希望单例实例在不再使用时自行完成清理工作。
    • 如果您实现了一个期望在正常程序执行期间进行清理的__del__()方法,则可能还需要删除atexit处理程序。
  3. 如果您希望在没有人再使用时清除单例,请考虑使用weakref将其存储在元类上,以使自己不保留它。

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