从列表中调用元素的析构函数

时间:2015-12-07 17:19:39

标签: python list destructor

我有类似的东西:

a = [instance1, instance2, ...]

如果我做了

del a[1]

instance2从列表中删除,但是实例2是名为?

的析构函数方法

我对此感兴趣,因为我的代码使用了大量内存,我需要释放内存从列表中删除实例。

3 个答案:

答案 0 :(得分:5)

来自像c ++这样的语言(正如我所做的那样),这往往是许多人在第一次学习Python时难以掌握的主题。

最重要的是:当您执行del XXX时,在使用del时,您永远不会*删除对象。您只是删除对象参考。但是,在实践中,假设没有其他引用存放于instance2对象,从列表中删除它将根据需要释放内存。

如果您不了解对象和对象引用之间的区别,请继续阅读。

Python:按值传递,或通过引用传递?

您可能熟悉通过引用或值将参数传递给函数的概念。但是,Python以不同的方式做事。参数始终由对象引用传递。请阅读this article,了解这意味着什么。

总结一下:这意味着当你将一个变量传递给一个函数时,你没有传递变量值的副本(按值传递),也没有传递对象本身 - 即,记忆中的价值。您正在传递间接引用内存中保存的值的名称对象。

这与del ...?

有什么关系?

Well, I'll tell you.

说你这样做:

def deleteit(thing):
    del thing

mylist = [1,2,3]
deleteit(mylist)

......你认为会发生什么?是否已从全局命名空间中删除mylist

答案是否定的:

assert mylist # No error

原因是当您在del thing函数中执行deleteit时,您只是将本地对象引用删除到该对象。该对象引用仅存在于函数内部。作为侧边栏,您可能会问:是否可以在函数内部从全局命名空间中删除对象引用?答案是肯定的,但您必须首先将对象引用声明为全局命名空间的一部分:

def deletemylist():
    global mylist
    del mylist

mylist = [1,2,3]
deletemylist()
assert mylist #ERROR, as expected

全部放在一起

现在回到原来的问题。在任何名称空间中,当您执行此操作时:

del XXX

...您尚未删除XXX表示的对象。你不能这样做。您只删除了对象引用 XXX,它引用了内存中的某个对象。对象本身由Python memory manager管理。这是一个非常重要的区别。

请注意,因此,当您覆盖某个对象的__del__方法时,会在 删除对象 时调用该方法(不是对象引用) !):

class MyClass():
    def __del__(self):
        print(self, "deleted")
        super().__del__()

m = MyClass()
del m

...执行__del__后,del m方法中的print语句不一定会立即发生。它只发生在删除对象本身的时间点,这不取决于你。这取决于Python内存管理器。删除所有名称空间中的所有对象引用后,最终将执行__del__方法。但不一定立即。

删除作为列表一部分的对象引用时也是如此,就像在原始示例中一样。执行del a[1]时,只删除a[1]表示的对象的对象引用,并且可以立即调用或不调用该对象的__del__方法(尽管如前所述,一旦没有对该对象的引用,它将最终被调用,并且该对象被内存管理器垃圾收集)。

因此,我们不建议您在__del__方法中使用del mything方法,因为它可能不会以这种方式发生。

*我相信它永远不会。不可避免地有人可能会回复我的回答,并发表评论,讨论该规则的例外情况。但是whatevs。

答案 1 :(得分:1)

没有。在列表元素上调用del仅从列表中删除对对象的引用,它不会(明确地)对对象本身执行任何操作。但是:如果列表中的引用是引用该对象的最后一个引用,则该对象现在可以被销毁和回收。我认为“正常”CPython实现会立即销毁和回收对象,其他变体的行为可能会有所不同。

答案 2 :(得分:0)

如果您的对象资源很多,并且您希望确保正确释放资源,请使用with()构造。在依赖析构函数时,很容易泄漏资源。有关详细信息,请参阅this SO post