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

    # ...

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

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


当前回答

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

其他回答

下面是一个最小的工作框架:

class SkeletonFixture:

    def __init__(self):
        pass

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        pass

    def method(self):
        pass


with SkeletonFixture() as fixture:
    fixture.method()

重要:返回自我


如果你像我一样,忽略了(克林特·米勒正确答案中的)“回归自我”部分,你将会看到下面这段废话:

Traceback (most recent call last):
  File "tests/simplestpossible.py", line 17, in <module>                                                                                                                                                          
    fixture.method()                                                                                                                                                                                              
AttributeError: 'NoneType' object has no attribute 'method'

希望它能帮助到下一个人。

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

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

我建议使用Python的with语句来管理需要清理的资源。使用显式close()语句的问题是,您必须担心人们完全忘记调用它,或者忘记将它放在finally块中以防止异常发生时资源泄漏。

要使用with语句,请使用以下方法创建一个类:

def __enter__(self)
def __exit__(self, exc_type, exc_value, traceback)

在上面的例子中,你可以使用

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

    def __enter__(self):
        return self

    # ...

    def __exit__(self, exc_type, exc_value, traceback):
        for file in self.files:
            os.unlink(file)

然后,当有人想要使用你的类时,他们会这样做:

with Package() as package_obj:
    # use package_obj

变量package_obj将是Package类型的实例(它是__enter__方法返回的值)。不管是否发生异常,它的__exit__方法都会被自动调用。

您甚至可以进一步使用这种方法。在上面的例子中,有人仍然可以使用它的构造函数实例化Package而不使用with子句。你不会希望发生这种事的。你可以通过创建一个PackageResource类来修复这个问题,这个类定义了__enter__和__exit__方法。然后,Package类将在__enter__方法中严格定义并返回。这样,调用者不使用with语句永远不能实例化Package类:

class PackageResource:
    def __enter__(self):
        class Package:
            ...
        self.package_obj = Package()
        return self.package_obj

    def __exit__(self, exc_type, exc_value, traceback):
        self.package_obj.cleanup()

你可以这样使用:

with PackageResource() as package_obj:
    # use package_obj

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

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