Python如何确保对象的__del__()方法在模块退出前被调用?

7 投票
3 回答
5249 浏览
提问于 2025-04-17 16:32

今天早些时候,我问了一个关于对象的 __del__() 方法的问题,这个对象使用了一个导入的模块。问题是 __del__() 方法想要使用 os 模块,但有时候(不是每次)这个模块已经被删除了。我被告知,当一个Python程序结束时,对象和模块被删除的顺序可能是随机的,而且是没有固定规则的。不过,对于我的应用来说,我真的需要确保一个对象(我创建的类的实例)在模块(这个对象被实例化的模块)被删除之前就被删除。有办法做到这一点吗?

3 个回答

0

之前提到的使用'atexit'的方法,也可以通过装饰器来实现,我觉得这特别有用。这样做的好处就是可以确保被装饰的函数会在程序结束时被调用。

@atexit.register
def close():
    print("Closing Program")
4

用一个关键词参数来引用这个函数:

import os

class Logger(object):    
    def __del__(self, os=os):
        print "os: %s." %os

或者把它绑定到一个类的属性上:

import os

class Logger(object):
    def __init__(self):
        self.os = os

    def __del__(self):
        print "os: %s." % self.os

通过创建一个局部(足够的)引用,Python 就不会把 os 当作全局变量去查找,而是会用一个局部引用,这些引用分别存储在实例或者函数本身里。

全局变量是在运行时查找的(这就是为什么你在另一个问题中提到的 __del__ 函数会失败,如果 os 已经被清理掉了),而函数的局部变量会和函数对象一起被清理,或者在实例属性的情况下,会和实例一起被清理。

15

正如我们之前提到的,你真的不应该依赖于在解释器退出时会调用 __del__。有两种正确的方法来处理这个问题:

第一种是使用 atexit

import os
import atexit

class Logger(object):   
    def on_exit(self):
        print "os: %s." % os

logger = Logger()
atexit.register(logger.on_exit)

这样可以确保你的日志记录器在程序退出时被正确关闭。


*进一步分析你的问题,由于你打算让一个实例绑定到一个定义该实例类的模块,因此下面的上下文管理器解决方案不适合你,因为在程序执行的整个过程中无法保持在这个上下文中。你需要使用 atexit.register。不过,从程序设计的角度来看,如果重构代码可以实现,我更倾向于使用上下文管理器来管理我的资源,而不是 atexit.register

第二种(更好的)方法是把你的类做成一个 上下文管理器,这样在退出上下文时就会执行清理代码。然后你的代码看起来会像这样:

import os
class Logger(object):
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_value, traceback):
        print "os:",str(os)

with Logger() as logger:
    #do something ...

撰写回答