1

我一直在寻找在 python 中清理对象的方法。我目前正在使用 pypy。

我找到了一个网页和一个例子。

首先是一个基本示例:

class FooType(object):
    def __init__(self, id):
        self.id = id
        print self.id, 'born'

    def __del__(self):
        print self.id, 'died'


ft = FooType(1)

这应该打印:

1 出生 1 死亡

但它只打印 1 出生

所以我的问题是:如何在 PyPy 中清理任何东西?

4

3 回答 3

4

当您需要在特定时间确定运行“清理”时,请使用上下文管理器

class FooType(object):
    def __init__(self, id):
        self.id = id
        print 'born'
    def __enter__(self):
        print 'entered'
        return self
    def __exit__(self, *exc):
        print 'exited'

with FooType(1) as ft:
    pass # do something with ft
于 2013-02-10T18:48:18.303 回答
4

与其他所有 Python 实现(包括 CPython )一样,您应该这样做:通过显式管理代码中的生命周期,而不是依赖自动内存管理和.__del__

即使在 CPython 中,也有不止一种情况__del__根本没有被调用,而且很多情况下调用它的时间比您预期的要晚得多(例如,任何时候任何对象都陷入引用循环中,这很容易发生)。这意味着它基本上是无用的,除了可能调试生命周期问题(但有内存分析器!),如果某些代码忽略显式清理,作为最后的手段。

通过明确清理,您可以回避所有这些问题。有一个进行清理的方法,并让客户端代码在正确的时间调用它。上下文管理器可以使这在面对异常时更容易正确处理,并且更具可读性。__del__即使引用计数“立即”调用它通常也允许更快地清理__del__。例如,这个:

def parse_file(path):
    f = open(path)
    return parse(f.read()) # file stays open during parsing

比这个 wrt 资源使用更糟糕:

def parse_file(path):
    with open(path) as f:
        s = f.read()
    # file is closed here
    return parse(s)

我还认为这样的设计更简洁,因为它不会混淆资源包装对象的生命周期和被包装资源的生命周期。有时,让该对象比资源寿命更长,甚至使其拥有新资源的所有权是有意义的。

于 2013-02-10T18:49:58.960 回答
2

在您的示例中,__del__未调用,但这只是因为您编写的测试程序立即完成。PyPy 保证__del__在对象不再可访问后的某个时间调用它,但前提是程序继续执行。所以如果你ft = FooType(1)在一个无限循环中做,它也会在一段时间后打印died出来。

正如其他答案所解释的那样,CPython 并不能真正保证任何事情,但在简单的情况下(例如,没有引用循环),它会__del__立即可靠地调用。不过,关键是您不应该严格依赖这一点。

于 2013-02-15T09:29:07.923 回答