我有类似的东西:
a = [instance1, instance2, ...]
如果我做了
del a[1]
instance2从列表中删除,但是实例2是名为?
的析构函数方法我对此感兴趣,因为我的代码使用了大量内存,我需要释放内存从列表中删除实例。
答案 0 :(得分:5)
来自像c ++这样的语言(正如我所做的那样),这往往是许多人在第一次学习Python时难以掌握的主题。
最重要的是:当您执行del XXX
时,在使用del
时,您永远不会*删除对象。您只是删除对象参考。但是,在实践中,假设没有其他引用存放于instance2
对象,从列表中删除它将根据需要释放内存。
如果您不了解对象和对象引用之间的区别,请继续阅读。
您可能熟悉通过引用或值将参数传递给函数的概念。但是,Python以不同的方式做事。参数始终由对象引用传递。请阅读this article,了解这意味着什么。
总结一下:这意味着当你将一个变量传递给一个函数时,你没有传递变量值的副本(按值传递),也没有传递对象本身 - 即,记忆中的价值。您正在传递间接引用内存中保存的值的名称对象。
del
...?说你这样做:
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。