【问题标题】:Call destructor of an element from a list从列表中调用元素的析构函数
【发布时间】:2016-03-12 09:37:50
【问题描述】:

我有类似的东西:

a = [instance1, instance2, ...]

如果我这样做

del a[1]

instance2 已从列表中移除,但是否调用了 instance2 的析构函数?

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

【问题讨论】:

  • this的相关/可能重复?
  • Python 是一种垃圾收集语言。如果您期望 del 与 C++ 的 delete 类似,那么您的期望是错误的。在 Python 中无法手动删除对象。

标签: python list destructor


【解决方案1】:

来自像 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

原因是当您在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__ 方法时,该方法会在 对象 被删除(而不是对象引用!)时调用:

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__ 方法中,因为那样可能不会发生。


*我相信永远不会。不可避免地有人会否决我的回答并发表评论讨论规则的例外情况。但是什么。

【讨论】:

  • 是的,几乎从来没有。要使del thing 实际删除对象而不是引用,您需要使用自定义映射类作为本地对象执行exec 之类的操作,并在删除时让自定义映射调用对象的tp_dealloc条目。这会破坏 Python 的许多规则,并可能导致线段错误。
  • 删除对对象的所有引用并调用 gc.collect() 也应该删除所有孤立对象。
  • 我忘记了 gc 模块(我从未使用过它)。
【解决方案2】:

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

【讨论】:

  • 好的,这正是我需要的 =) 谢谢!
【解决方案3】:

如果您的对象是资源密集型的,并且您想确保资源被正确释放,请使用with() 构造。依赖析构函数时很容易泄漏资源。详情请见this SO post

【讨论】:

    猜你喜欢
    • 2017-02-15
    • 1970-01-01
    • 2014-05-27
    • 2021-09-24
    • 2016-09-06
    • 1970-01-01
    • 2016-01-24
    • 2015-03-29
    • 2021-05-18
    相关资源
    最近更新 更多