2009-10-02 52 views
42

适当地使用weakref我有一些代码,其中的类的实例有家长< - >子引用对方,如:如何以及何时在Python

class Node(object): 
    def __init__(self): 
    self.parent = None 
    self.children = {} 
    def AddChild(self, name, child): 
    child.parent = self 
    self.children[name] = child 

def Run(): 
    root, c1, c2 = Node(), Node(), Node() 
    root.AddChild("first", c1) 
    root.AddChild("second", c2) 
Run() 

认为这将创建循环引用,使得在Run()完成后,root,c1c2不会被释放,对吗?那么,如何让他们获得释放?我想我可以做点像root.children.clear()self.parent = None - 但是如果我不知道什么时候该做什么?

这是一个适当的时间来使用weakref模块?什么,确切地说,我弱化refrefify? parent属性? children属性?整个对象?上述所有的?我看到关于WeakKeyDictionary和weakref.proxy的讨论,但我不清楚在这种情况下应该如何使用它们。

这也是python2.4(不能升级)。

更新:互相依赖的例子和总结

什么反对weakref-IFY取决于哪个对象生活中可以没有其他的,什么对象。寿命最长的物体应该包含较短寿命物体的弱点。类似地,不应该对依赖关系进行弱引发 - 如果是这样,依赖关系可能会默默地消失,即使它仍然是需要的。

如果,例如,你有一个树状结构,root,有孩子,kids,但可以存在没有孩子,那么root对象应使用weakrefs其kids。如果子对象取决于父对象的存在,情况也是如此。在下面,子对象要求父母为来计算其深度,因此为parent的强引用。尽管kids属性的成员是可选的,但是使用weakrefs来防止循环引用。

class Node: 
    def __init__(self) 
    self.parent = None 
    self.kids = weakref.WeakValueDictionary() 
    def GetDepth(self): 
    root, depth = self, 0 
    while root: 
     depth += 1 
     root = root.parent 
    return depth 
root = Node() 
root.kids["one"] = Node() 
root.kids["two"] = Node() 
# do what you will with root or sub-trees of it. 

要翻转关系,我们有如下所示。在这里,Facade类需要一个Subsystem实例才能工作,所以他们使用强引用到他们需要的子系统。然而,Subsystem不需要Facade工作。 Subsystem只是提供一种方式来通知Facade关于彼此的行为。

class Facade: 
    def __init__(self, subsystem) 
    self.subsystem = subsystem 
    subsystem.Register(self) 

class Subsystem: 
    def __init__(self): 
    self.notify = [] 
    def Register(self, who): 
    self.notify.append(weakref.proxy(who)) 

sub = Subsystem() 
f1 = CliFacade(sub) 
f2 = WebFacade(sub) 
# Go on to reading from POST, stdin, etc 

回答

25

是的,weakref在这里很棒。具体来说,而不是:

self.children = {} 

使用:

self.children = weakref.WeakValueDictionary() 

没有别的需要,更改您的代码。这样,当一个孩子没有其他差异时,它就会消失 - 父母的children地图中的条目也会以该孩子为值。

避免参考循环与实现缓存一样高,以此作为使用weakref模块的动机。参考循环不会杀死你,但它们可能最终会堵塞你的记忆,尤其是你的记忆。如果其中涉及实例的某些类定义为__del__,则会干扰gc的模块解散这些循环的能力。

+0

另外,如果您确定不需要循环gc,则可以禁用它以实现小的性能提升。 – 2009-10-02 05:11:50

+1

谢谢,亚历克斯。是否有一个特定的原因来弱报告“children”而不是'parent'?效果会一样吗?如果'父母'也是弱refref会发生什么?在双链表的情况下,“prev”,“next”还是两者都应该是weakrefs? – 2009-10-02 08:08:08

+5

这是不好的建议。示例中的所有子节点将在从Run()返回后立即被破坏。一般来说,你几乎总是把一个结构体的根绑定到变量上,所以正确的方法是将'weakref'用于'parent',而不是'children'。 – 2009-10-02 08:10:04

13

我建议使用child.parent = weakref.proxy(self)。在(外部参照)parent的使用期限涵盖child的使用期限的情况下,这是避免循环参考的好办法。相反,当child的使用期限涵盖parent的使用期限时,使用weakref代替child(如Alex建议)。但是,如果parentchild都不存在,则从不使用weakref

这里用示例说明这些规则。使用weakref-ED父母,如果你存储的根在一些变量,并通过它,而孩子们从中访问:如果他们都绑定到变量

def Run(): 
    root, c1, c2 = Node(), Node(), Node() 
    root.AddChild("first", c1) 
    root.AddChild("second", c2) 
    return root # Note that only root refers to c1 and c2 after return, 
       # so this references should be strong 

使用weakref-ED的孩子,而根则通过他们访问:

def Run(): 
    root, c1, c2 = Node(), Node(), Node() 
    root.AddChild("first", c1) 
    root.AddChild("second", c2) 
    return c1, c2 

但也不会为以下工作:

def Run(): 
    root, c1, c2 = Node(), Node(), Node() 
    root.AddChild("first", c1) 
    root.AddChild("second", c2) 
    return c1 
+2

有很多情况下,只要其他实体仍然持有对他们的引用,孩子和父母中的任何一方或两方都可以在没有其他实体的情况下活着。那就是当你可能想要使用相互弱的引用时(那些其他的外部引用将会根据需要完成保持实体活动的工作)。 – 2009-10-02 14:44:11

+2

**“我建议使用'child.parent = weakref.proxy(self)'。”** _Thisss._这是一个常见情况的典型方法,其中一个长寿命的“父”包含多个短命的孩子'-ren。亚历克西斯的解决方案更适用于拥有“一个短暂的”父母“的多重长寿'子女'的边缘案例,这在我看来是很少见的。 – 2016-01-29 06:39:07

1

我想澄清的引用可能很弱。以下方法是通用的,但是我在所有示例中使用了双链树。

合乎逻辑的步骤1.

你需要确保有很强的参考,以保持所有对象,只要活着,你需要他们。它可以在许多方面的工作要做,例如:

  • [直接名称]:命名的参照每个节点树
  • [容器]:到容器的引用存储所有节点
  • 从每个节点到其子
  • [叶+父]到根节点的引用,和参考文献:
  • [根+儿童]到所有叶节点的引用,以及从每个节点到其父
引用

逻辑步骤2.

如果需要,现在您添加引用来表示信息。例如,如果您在步骤1中使用[容器]方法,则仍然需要表示边缘。节点A和B之间的边缘可以用单个参考来表示;它可以沿任何方向前进。同样,也有多种选择,例如:

  • [儿童]:从每个节点到其子引用
  • [父]:从每个节点到它的父参考
  • [集合的集合] :包含2元素集合的集合;每个2元素包含引用一个边缘

当然的节点,如果使用[根+儿童]在步骤1中的方法,所有的信息已经充分表示,因此跳过这一步。

逻辑步骤3.

现在您添加引用以提高性能,如果需要的话。例如,如果您在步骤1中使用[容器]方法,而在步骤2中使用[子项]方法,则可能希望提高某些算法的速度,并在每个节点与其父项之间添加引用。这些信息在逻辑上是多余的,因为您可以(以性能为代价)从现有数据中获取它。


所有步骤1中的引用必须要坚强

步骤2和3中的所有参考文献可能较弱或较强。使用强引用没有优势。使用弱引用有一个好处,直到你知道周期不再可能。严格地说,一旦你知道周期是不可能的,那么使用弱还是强参考都没有区别。但是为了避免考虑它,你也可以在步骤2和步骤3中只使用弱引用。

相关问题