调用列表中元素的析构函数
Call destructor of an element from a list
我有类似的东西:
a = [instance1, instance2, ...]
如果我做一个
del a[1]
instance2 已从列表中删除,但是否调用了 instance2 的 destructor 方法?
我对此很感兴趣,因为我的代码使用了大量内存,我需要释放内存以从列表中删除实例。
如果您的对象占用大量资源并且您希望确保正确释放资源,请使用 with()
结构。依赖析构函数时很容易泄漏资源。有关详细信息,请参阅 this SO post。
没有。在列表元素上调用 del
只会从列表中删除对对象的引用,它不会(明确地)对对象本身做任何事情。但是:如果列表中的引用是引用该对象的最后一个,那么现在可以销毁并回收该对象。我认为 "normal" CPython 实现会立即销毁并回收对象,其他变体的行为可能会有所不同。
来自像 c++ 这样的语言(就像我一样),这往往是许多人在第一次学习时发现难以掌握的主题 Python。
底线是:当您执行 del XXX
时,您永远不会*在使用 del
时删除 对象 。您只是在删除一个 对象引用 。但是,实际上,假设没有其他引用指向 instance2
对象,将其从列表中删除将根据需要释放内存。
如果您不理解对象和对象引用之间的区别,请继续阅读。
Python:按值传递,还是按引用传递?
您可能熟悉通过引用或值将参数传递给函数的概念。但是,Python 做事的方式不同。参数总是通过 对象引用 传递。阅读 this article 以获得对这意味着什么的有用解释。
总而言之:这意味着当您将变量传递给函数时,您没有传递变量值的副本(按值传递),也没有传递对象本身 - 即地址内存中的值。您正在传递间接引用内存中保存的值的名称对象。
这与 del
...有什么关系?
假设你这样做:
def deleteit(thing):
del thing
mylist = [1,2,3]
deleteit(mylist)
...你认为会发生什么? mylist
是否已从全局命名空间中删除?
答案是否定的:
assert mylist # No error
原因是当您在 deleteit
函数中执行 del thing
时,您只是删除了 对该对象的本地对象引用 。该对象引用仅存在于函数内部。作为侧边栏,您可能会问:是否可以在函数内部从全局命名空间中删除对象引用?答案是肯定的,但是你必须首先声明对象引用是全局命名空间的一部分:
def deletemylist():
global mylist
del mylist
mylist = [1,2,3]
deletemylist()
assert mylist #ERROR, as expected
综合起来
现在回到你原来的问题。当在任何名称空间中执行此操作时:
del XXX
...您尚未删除由 XXX 表示的对象。你不能那样做。您只删除了 object reference XXX
,它引用了内存中的某个对象。对象本身由 Python memory manager 管理。这是一个非常重要的区别。
请注意,因此,当您覆盖某个对象的 __del__
方法时,该方法在 对象 [=84= 时被调用] 被删除(不是对象引用!):
class MyClass():
def __del__(self):
print(self, "deleted")
super().__del__()
m = MyClass()
del m
...__del__
方法中的 print 语句不一定会在您执行 del m
之后立即出现。它只发生在对象本身被删除的时间点,这不取决于你。这取决于 Python 内存管理器。当所有命名空间中的所有对象引用都被删除后,最终会执行 __del__
方法。但不一定立即。
删除作为列表一部分的对象引用时也是如此,就像在原始示例中一样。当您执行 del a[1]
时,仅删除由 a[1]
表示的对象的对象引用,并且可能会或可能不会立即调用该对象的 __del__
方法(尽管如前所述,一旦不再有对该对象的引用,它最终将被调用,并且该对象被内存管理器收集为垃圾)。
因此,不建议您将希望在 del mything
上立即发生的事情放在 __del__
方法中,因为这样可能不会发生。
*我相信永远不会。不可避免地,有人可能会对我的回答投反对票,并发表评论讨论规则的例外情况。但无论如何。
我有类似的东西:
a = [instance1, instance2, ...]
如果我做一个
del a[1]
instance2 已从列表中删除,但是否调用了 instance2 的 destructor 方法?
我对此很感兴趣,因为我的代码使用了大量内存,我需要释放内存以从列表中删除实例。
如果您的对象占用大量资源并且您希望确保正确释放资源,请使用 with()
结构。依赖析构函数时很容易泄漏资源。有关详细信息,请参阅 this SO post。
没有。在列表元素上调用 del
只会从列表中删除对对象的引用,它不会(明确地)对对象本身做任何事情。但是:如果列表中的引用是引用该对象的最后一个,那么现在可以销毁并回收该对象。我认为 "normal" CPython 实现会立即销毁并回收对象,其他变体的行为可能会有所不同。
来自像 c++ 这样的语言(就像我一样),这往往是许多人在第一次学习时发现难以掌握的主题 Python。
底线是:当您执行 del XXX
时,您永远不会*在使用 del
时删除 对象 。您只是在删除一个 对象引用 。但是,实际上,假设没有其他引用指向 instance2
对象,将其从列表中删除将根据需要释放内存。
如果您不理解对象和对象引用之间的区别,请继续阅读。
Python:按值传递,还是按引用传递?
您可能熟悉通过引用或值将参数传递给函数的概念。但是,Python 做事的方式不同。参数总是通过 对象引用 传递。阅读 this article 以获得对这意味着什么的有用解释。
总而言之:这意味着当您将变量传递给函数时,您没有传递变量值的副本(按值传递),也没有传递对象本身 - 即地址内存中的值。您正在传递间接引用内存中保存的值的名称对象。
这与 del
...有什么关系?
假设你这样做:
def deleteit(thing):
del thing
mylist = [1,2,3]
deleteit(mylist)
...你认为会发生什么? mylist
是否已从全局命名空间中删除?
答案是否定的:
assert mylist # No error
原因是当您在 deleteit
函数中执行 del thing
时,您只是删除了 对该对象的本地对象引用 。该对象引用仅存在于函数内部。作为侧边栏,您可能会问:是否可以在函数内部从全局命名空间中删除对象引用?答案是肯定的,但是你必须首先声明对象引用是全局命名空间的一部分:
def deletemylist():
global mylist
del mylist
mylist = [1,2,3]
deletemylist()
assert mylist #ERROR, as expected
综合起来
现在回到你原来的问题。当在任何名称空间中执行此操作时:
del XXX
...您尚未删除由 XXX 表示的对象。你不能那样做。您只删除了 object reference XXX
,它引用了内存中的某个对象。对象本身由 Python memory manager 管理。这是一个非常重要的区别。
请注意,因此,当您覆盖某个对象的 __del__
方法时,该方法在 对象 [=84= 时被调用] 被删除(不是对象引用!):
class MyClass():
def __del__(self):
print(self, "deleted")
super().__del__()
m = MyClass()
del m
...__del__
方法中的 print 语句不一定会在您执行 del m
之后立即出现。它只发生在对象本身被删除的时间点,这不取决于你。这取决于 Python 内存管理器。当所有命名空间中的所有对象引用都被删除后,最终会执行 __del__
方法。但不一定立即。
删除作为列表一部分的对象引用时也是如此,就像在原始示例中一样。当您执行 del a[1]
时,仅删除由 a[1]
表示的对象的对象引用,并且可能会或可能不会立即调用该对象的 __del__
方法(尽管如前所述,一旦不再有对该对象的引用,它最终将被调用,并且该对象被内存管理器收集为垃圾)。
因此,不建议您将希望在 del mything
上立即发生的事情放在 __del__
方法中,因为这样可能不会发生。
*我相信永远不会。不可避免地,有人可能会对我的回答投反对票,并发表评论讨论规则的例外情况。但无论如何。