【问题标题】:Python: deletion of self referencing objectPython:删除自引用对象
【发布时间】:2014-12-24 07:50:03
【问题描述】:

想问一下如何在Python中删除具有自引用的对象

让我们考虑一个类,这是一个知道何时创建和何时删除的简单示例:

#!/usr/bin/python
class TTest:
  def __init__(self):
    self.sub_func= None
    print 'Created',self
  def __del__(self):
    self.sub_func= None
    print 'Deleted',self
  def Print(self):
    print 'Print',self

这个类有一个变量self.sub_func,我们假设给它分配一个函数。我想为self.sub_func 分配一个使用TTest 实例的函数。请看以下案例:

def SubFunc1(t):
  t.Print()
def DefineObj1():
  t= TTest()
  t.sub_func= lambda: SubFunc1(t)
  return t

t= DefineObj1()
t.sub_func()
del t

结果是:

Created <__main__.TTest instance at 0x7ffbabceee60>
Print <__main__.TTest instance at 0x7ffbabceee60>

也就是说,虽然我们执行了“del t”,但t并没有被删除

我猜是因为 t.sub_func 是一个自引用对象,所以 t 的引用计数器不会在“del t”处变为零,因此 t 不会被垃圾收集器删除。

要解决这个问题,我需要插入

t.sub_func= None

在“del t”之前;此时,输出为:

Created <__main__.TTest instance at 0x7fab9ece2e60>
Print <__main__.TTest instance at 0x7fab9ece2e60>
Deleted <__main__.TTest instance at 0x7fab9ece2e60>

但这很奇怪。 t.sub_func 是 t 的一部分,所以我不想关心删除 t 时清除 t.sub_func

如果你知道一个好的解决方案,你能告诉我吗?

【问题讨论】:

  • 你为什么关心这个?你能用weakref吗?
  • Python 垃圾收集器可以检测循环。在多年的 python 编程中,我从未使用过弱引用,而且我很少需要显式删除。你确定你需要吗?你有记忆问题吗?
  • 也许在del之后调用gc.collect()
  • 谢谢你们。我测试了 gc.collect() 但它没有用。我的 python 版本是 2.7.5+。其实到现在我都是依赖python的GC,但是我发现我的一些想要释放的对象因为这个问题没有释放。其中一些正在使用大内存。我希望这样一个对象在函数结束时释放,但它仍然存在,所以我需要重写每个代码,这很奇怪。
  • 我还没有考虑过weakref。

标签: python self-reference


【解决方案1】:

如何确保引用循环中的对象在不再可访问时被删除?最简单的解决方案是不定义__del__ 方法。如果有的话,很少有类需要__del__ 方法。 Python 不保证何时甚至是否会调用 __del__ 方法。

有几种方法可以缓解这个问题。

  1. 使用包含和检查弱引用的函数而不是 lambda。每次调用函数时都需要明确检查对象是否仍然存在。
  2. 为每个对象创建一个唯一的类,以便我们可以将函数存储在一个类中,而不是作为猴子修补函数。这可能会占用大量内存。
  3. 定义一个知道如何获取给定函数并将其转换为方法的属性。我个人最喜欢它,因为它非常近似于如何从类的未绑定方法创建绑定方法。

使用弱引用

import weakref

class TTest:
    def __init__(self):
        self.func = None
        print 'Created', self
    def __del__(self):
        print 'Deleted', self
    def print_self(self):
        print 'Print',self

def print_func(t):
    t.print_self()

def create_ttest():
    t = TTest()
    weak_t = weakref.ref(t)
    def func():
        t1 = weak_t()
        if t1 is None:
            raise TypeError("TTest object no longer exists")
        print_func(t1)
    t.func = func
    return t

if __name__ == "__main__":
    t = create_ttest()
    t.func()
    del t

创建一个独特的类

class TTest:
    def __init__(self):
        print 'Created', self
    def __del__(self):
        print 'Deleted', self
    def print_self(self):
        print 'Print',self

def print_func(t):
    t.print_self()

def create_ttest():
    class SubTTest(TTest):
        def func(self):
            print_func(self)
    SubTTest.func1 = print_func 
    # The above also works. First argument is instantiated as the object the 
    # function was called on.
    return SubTTest()

if __name__ == "__main__":
    t = create_ttest()
    t.func()
    t.func1()
    del t

使用属性

import types

class TTest:
    def __init__(self, func):
        self._func = func
        print 'Created', self
    def __del__(self):
        print 'Deleted', self
    def print_self(self):
        print 'Print',self
    @property
    def func(self):
        return types.MethodType(self._func, self)

def print_func(t):
    t.print_self()

def create_ttest():
    def func(self):
        print_func(self)
    t = TTest(func)
    return t

if __name__ == "__main__":
    t = create_ttest()
    t.func()
    del t

【讨论】:

  • 感谢您的三个想法。我最喜欢第一个(使用弱引用),因为它代码少,易于理解(接近 C/C++ 的指针),并且直观。我认为,就我而言,对象存在的显式检查是不必要的,因为“func”总是作为“t.func”执行,这意味着t(及其弱引用)存在。跨度>
【解决方案2】:

来自the official CPython docs

具有__del__() 方法并且是引用循环的一部分的对象会导致整个引用循环无法收集,包括不一定在循环中但只能从中访问的对象。 Python 不会自动收集此类循环,因为通常 Python 无法猜测运行 __del__() 方法的安全顺序。如果您知道安全顺序,则可以通过检查垃圾列表来强制执行该问题,并根据列表中的对象显式中断循环。请注意,由于位于垃圾列表中,这些对象仍然保持活动状态,因此它们也应该从垃圾中删除。例如,在中断循环后,执行del gc.garbage[:] 以清空列表。通常最好通过不使用 __del__() 方法创建包含对象的循环来避免此问题,并且在这种情况下可以检查垃圾以验证没有创建此类循环。

另见:http://engineering.hearsaysocial.com/2013/06/16/circular-references-in-python/

【讨论】:

  • 年,从技术上讲,我的问题似乎是一种循环引用。但它的实际含义显然是不同的。有问题的部分有一个层次结构: t.sub_func= lambda: SubFunc1(t); SubFunc1(t) 是 t 的子元素。问题是python无法知道这个层次结构。你认为使用weakref 是告诉python 的正确方法吗?
  • 您需要__del__ 方法吗?很少有课这样做。如果循环中涉及的所有对象都没有明确的__del__,Python 可以很好地垃圾收集它们。只有当至少存在一个__del__ 方法时,解释器才会拒绝收集循环,因为它无法猜测__del__ 方法本身需要哪些部分。
猜你喜欢
  • 2012-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-28
  • 2022-06-14
  • 1970-01-01
  • 2023-02-14
相关资源
最近更新 更多