Python如何确保对象的__del__()方法在模块退出前被调用?
今天早些时候,我问了一个关于对象的 __del__()
方法的问题,这个对象使用了一个导入的模块。问题是 __del__()
方法想要使用 os
模块,但有时候(不是每次)这个模块已经被删除了。我被告知,当一个Python程序结束时,对象和模块被删除的顺序可能是随机的,而且是没有固定规则的。不过,对于我的应用来说,我真的需要确保一个对象(我创建的类的实例)在模块(这个对象被实例化的模块)被删除之前就被删除。有办法做到这一点吗?
3 个回答
之前提到的使用'atexit'的方法,也可以通过装饰器来实现,我觉得这特别有用。这样做的好处就是可以确保被装饰的函数会在程序结束时被调用。
@atexit.register
def close():
print("Closing Program")
用一个关键词参数来引用这个函数:
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
已经被清理掉了),而函数的局部变量会和函数对象一起被清理,或者在实例属性的情况下,会和实例一起被清理。
正如我们之前提到的,你真的不应该依赖于在解释器退出时会调用 __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 ...