class Package:
    def __init__(self):
        self.files = []

    # ...

    def __del__(self):
        for file in self.files:
            os.unlink(file)

上面的__del__(self)失败,出现AttributeError异常。我理解当调用__del__()时,Python不保证存在“全局变量”(在此上下文中是成员数据?)如果是这种情况,这是异常的原因,我如何确保对象销毁正确?


当前回答

标准的方法是使用atexit.register:

# package.py
import atexit
import os

class Package:
    def __init__(self):
        self.files = []
        atexit.register(self.cleanup)

    def cleanup(self):
        print("Running cleanup...")
        for file in self.files:
            print("Unlinking file: {}".format(file))
            # os.unlink(file)

但是您应该记住,这将保存所有已创建的Package实例,直到Python终止。

演示使用上述代码保存为package.py:

$ python
>>> from package import *
>>> p = Package()
>>> q = Package()
>>> q.files = ['a', 'b', 'c']
>>> quit()
Running cleanup...
Unlinking file: a
Unlinking file: b
Unlinking file: c
Running cleanup...

其他回答

更好的替代方法是使用weakref.finalize。请参阅终结器对象和用__del__()方法比较终结器的示例。

我认为问题可能是在__init__如果有更多的代码比显示?

即使__init__没有正确执行或抛出异常,__del__也将被调用。

我认为在__del__被调用之前,实例成员不可能被移除。我的猜测是,您的特定AttributeError的原因是在其他地方(可能您错误地删除了self。文件在其他地方)。

然而,正如其他人指出的那样,你应该避免使用__del__。主要原因是带有__del__的实例不会被垃圾收集(只有当它们的refcount达到0时才会被释放)。因此,如果你的实例涉及循环引用,那么只要应用程序运行,它们就会一直存在于内存中。(我可能对所有这些都错了,我必须再次阅读gc文档,但我相当肯定它是这样工作的)。

atexit。在ostrakach的回答中已经提到过,寄存器是标准的方式。

但是,必须注意的是,不能依赖于对象可能被删除的顺序,如下例所示。

import atexit

class A(object):

    def __init__(self, val):
        self.val = val
        atexit.register(self.hello)

    def hello(self):
        print(self.val)


def hello2():
    a = A(10)

hello2()    
a = A(20)

在这里,顺序似乎是合法的,因为它与创建对象的顺序相反,程序给出的输出如下:

20
10

然而,在一个较大的程序中,当python的垃圾收集启动超出其生命周期的对象时,将首先被销毁。

这样做的惯用方法似乎是提供一个close()方法(或类似方法),并显式地调用它。