在PyPy中进行清理

1 投票
3 回答
574 浏览
提问于 2025-04-17 15:25

我一直在寻找在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中,我该如何清理任何东西呢?

3 个回答

2

在你的例子中,__del__ 这个方法没有被调用,但这只是因为你写的测试程序马上就结束了。PyPy 确保在对象不再被使用后,__del__ 会在某个时刻被调用,但前提是程序还在运行。所以如果你在一个无限循环中执行 ft = FooType(1),过一段时间后,它也会打印出 died

正如其他回答所说,CPython 并不真的保证什么,但在简单的情况下(比如没有引用循环),它会立即并可靠地调用 __del__。不过,重点是你不应该完全依赖这个。

4

你应该像在其他所有Python实现中一样,包括CPython,通过代码明确管理对象的生命周期,而不是依赖自动内存管理和__del__

即使在CPython中,也有很多情况是__del__根本不会被调用,或者被调用的时间比你预期的要晚得多(比如,当某个对象陷入引用循环时,这种情况很容易发生)。这意味着__del__基本上没什么用,除了可能用来调试生命周期问题(不过有专门的内存分析工具可以做到这一点!),以及在某些代码忘记显式清理时作为最后的手段

通过明确进行清理,你可以避免这些问题。你可以创建一个专门负责清理的函数,然后让其他代码在合适的时机调用它。使用上下文管理器可以让你在处理异常时更容易做到这一点,同时也让代码更易读。这样做通常还可以比__del__更早地进行清理,即使引用计数“立即”调用__del__。例如,这段代码:

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

在资源使用方面比这段代码要差:

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

我还认为这样的设计更清晰,因为它不会把资源包装对象的生命周期和被包装资源的生命周期混淆。有时候,让这个对象的生命周期比资源长是有意义的,或者甚至让它拥有一个新的资源。

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

撰写回答