Skip to main content

对 weakref 的一点理解

·198 words·1 min
class Node(object):

    def __init__(self, data):
        self.data = data
        self.parent = None
        self.children = []

    def add_child(self, child):
        self.children.append(child)
        child.parent = self

    def __del__(self):
        print '__del__'


n = Node(0)
del n
# __del__
n1 = Node(1)
n2 = Node(2)
n1.add_child(n2)
del n1 # no output
n2.parent
# <__main__.Node at 0x7fd87ad5c250>

双亲节点的指针指向孩子节点,孩子节点又指向双亲节点。这构成了循环引用,所以每个对象的引用计数都不可能变成 0

我们可以手动使用 gc 模块来进行垃圾回收

import gc

gc.collect()

糟糕的是,我们这里循环引用的对象都实现了 __del__ 方法,gc 模块不会销毁这些对象,因为 gc 模块不知道应该先调用哪个对象的 __del__ 方法。gc模块会把这样的对象放到 gc.garbage 中,并不会销毁对象。

n1 = Node(1)
n2 = Node(2)
print n1, n2
# <__main__.Node object at 0x7f94109906d0> <__main__.Node object at 0x7f9410990610>
n1.add_child(n2)
del n1
del n2
gc.collect()
# 64
gc.garbage
# [<__main__.Node object at 0x7f94109906d0> <__main__.Node object at 0x7f9410990610>]

我们可以通过 weakref 来解决,如果一个对象只剩下一个弱引用,那么它是可以被垃圾回收的

import weakref


class Node(object):

    def __init__(self, data):
        self.data = data
        self._parent = None
        self.children = []

    @property
    def parent(self):
        return None if self._parent is None else self._parent()

    @parent.setter
    def parent(self, node):
        self._parent = weakref.ref(node, callback)

    def add_child(self, child):
        self.children.append(child)
        child.parent = self


def callback(ref):
    print '__del__', ref


n1 = Node(0)
n2 = Node(2)
print n1,n2
# <__main__.Node object at 0x7fb0c2750c10> <__main__.Node object at 0x7fb0c2750d10>
n1.add_child(n2)
del n1
# __del__ <weakref at 0x7fb0c26e75d0; dead>

不过,如果我们使用 weakref.ref() 创建弱引用,每次使用时都需要形如这样 xx() 来获取,有一点别扭。 可以通过 weakref.proxy() 这种来避免 () 操作。

n = Node(10)
p = weakref.proxy(n)
p.data
# 10