Python:删除自引用对象

时间:2014-12-23 21:30:07

标签: python self-reference

我想问一下如何在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,我们假设它分配一个函数。我想将一个函数使用TTest 的实例分配给self.sub_func。请参阅以下案例:

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>

也就是说,虽然我们执行了&#34; del t&#34;,但t未被删除

我猜原因是t.sub_func是一个自引用对象,因此t的引用计数器在&#34; del t&#34;时不会变为零,因此垃圾收集器不会删除t。 / p>

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

t.sub_func= None

之前&#34; del t&#34 ;;在这个时候,输出是:

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。

你能告诉我你是否知道一个好的解决方案?

2 个答案:

答案 0 :(得分:2)

来自the official CPython docs

  

具有__del__()方法且属于引用循环的对象会导致整个引用循环无法收集,包括不一定在循环中但只能从其中访问的对象。 Python不会自动收集这样的循环,因为通常情况下,Python无法猜测运行__del__()方法的安全顺序。如果您知道安全订单,则可以通过检查垃圾清单来强制解决问题,并明确地打破由于列表中的对象而导致的周期。请注意,这些对象即使存在于垃圾列表中也会保持活动状态,因此它们也应该从垃圾中删除。例如,在打破周期后,执行del gc.garbage[:]清空列表。通常不要通过不创建包含__del__()方法的对象的循环来避免这个问题,在这种情况下可以检查垃圾以验证是否没有创建这样的循环。

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

答案 1 :(得分:2)

如何确保参考周期中的对象在不再可访问时被删除?最简单的解决方案是不定义__del__方法。很少(如果有)类需要__del__方法。 Python不保证何时或甚至调用__del__方法。

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

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

    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