如何正确清理Python对象?

2024-03-29 15:29:43 发布

您现在位置:Python中文网/ 问答频道 /正文

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

    # ...

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

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


Tags: inselfpackageforinitosdeffiles
3条回答

作为Clint's answer的附录,可以使用^{}简化PackageResource

@contextlib.contextmanager
def packageResource():
    class Package:
        ...
    package = Package()
    yield package
    package.cleanup()

或者,虽然可能不是Pythonic,但是可以重写Package.__new__

class Package(object):
    def __new__(cls, *args, **kwargs):
        @contextlib.contextmanager
        def packageResource():
            # adapt arguments if superclass takes some!
            package = super(Package, cls).__new__(cls)
            package.__init__(*args, **kwargs)
            yield package
            package.cleanup()

    def __init__(self, *args, **kwargs):
        ...

只需使用with Package(...) as package

若要缩短时间,请将清理函数命名为close,然后使用^{},在这种情况下,可以通过with contextlib.closing(Package(...))使用未修改的Package类,也可以将其__new__重写为更简单的

class Package(object):
    def __new__(cls, *args, **kwargs):
        package = super(Package, cls).__new__(cls)
        package.__init__(*args, **kwargs)
        return contextlib.closing(package)

这个构造函数是继承的,所以您可以简单地继承,例如

class SubPackage(Package):
    def close(self):
        pass

我建议使用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__方法都将被自动调用。

你甚至可以更进一步。在上面的示例中,有人仍然可以使用其构造函数实例化包,而不使用with子句。你不想发生这种事。可以通过创建定义__enter____exit__方法的PackageResource类来解决此问题。然后,包类将严格定义在__enter__方法内并返回。这样,调用方就无法在不使用with语句的情况下实例化包类:

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

标准方法是使用^{}

# 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...

相关问题 更多 >