如何以及何时在Python中正确使用weakref

Ric*_*eur 49 python circular-reference

我有一些代码,其中类的实例具有父< - >子引用,例如:

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 Code Online (Sandbox Code Playgroud)

认为这会创建循环引用root,c1并且c2在Run()完成后不会被释放,对吗?那么,如何让它们被释放?我想我可以做点什么root.children.clear(),或者self.parent = None- 但如果我不知道该怎么做呢?

这是使用weakref模块的合适时间吗?什么,我究竟是什么弱反应?该parent属性?该children属性?整个对象?上述所有的?我看到有关WeakKeyDictionary和weakref.proxy的讨论,但我不清楚它们应该如何使用,如果有的话,在这种情况下.

这也是在python2.4上(无法升级).

更新:示例和摘要

weakref-ify的哪些对象取决于哪个对象可以在没有另一个对象的情况下生存,以及哪些对象相互依赖.生命时间最长的对象应包含较短寿命对象的弱化参数.同样,不应该将weakrefs设置为依赖项 - 如果它们是依赖项,依赖项可能会默默地消失,即使它仍然需要.

例如,如果你有一个树结构,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.
Run Code Online (Sandbox Code Playgroud)

为了改变这种关系,我们有类似下面的内容.这里,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
Run Code Online (Sandbox Code Playgroud)

Ale*_*lli 30

是的,weakref在这里非常出色.具体而言,而不是:

self.children = {}
Run Code Online (Sandbox Code Playgroud)

使用:

self.children = weakref.WeakValueDictionary()
Run Code Online (Sandbox Code Playgroud)

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

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

  • 这是一个糟糕的建议.从"Run()"返回后,示例中的所有子节点都将被销毁.一般来说,你几乎总是将结构的根绑定到变量,所以正确的方法是使用`weakref`作为`parent`,而不是`child`. (6认同)
  • 我还发现这条消息建议弱根用于root而不是离开:http://74.125.155.132/search?q = cache:http://mail.python.org/pipermail/python-list/2009-March/705913 html的 (3认同)
  • 关键不是"父母"或"孩子",而是"哪一个可以不存在另一个".在这个玩具示例中没有什么迹象,除了我们_know_父亲_can_没有孩子存在(因为它以这种方式开始!)但我们不知道反过来是否正确 - 也许这些孩子本质上需要来自父母的一些服务,如果是这样,他们应该正常参考AKA强引用它.@Denis,当然在这个玩具示例中,所有东西(父母和孩子们)都会在Run结束时被摧毁 - d'oh:这就是**制作它的玩具,并没有使我的建议无效. (2认同)

Den*_*ach 18

我建议使用child.parent = weakref.proxy(self).这是避免循环引用的好方法,以防(外部引用)的parent生命周期覆盖生命周期child.相反,weakref用于child(如亚历克斯建议的)寿命的child覆盖寿命parent.但是从来没有使用过weakref,parent并且child没有其他的可以活着.

这些规则用例子说明.如果将root存储在某个变量中并使用它,则使用weakref-ed parent,同时从中访问子节点:

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
Run Code Online (Sandbox Code Playgroud)

如果将所有子项绑定到变量,则使用weakref-ed子项,而通过它们访问root:

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

但两种方法都不适用于以下方面:

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

  • **"我建议使用`child.parent = weakref.proxy(self)`."**_Thisss._这是一个规范的方法,用于包含多个短命的`child`的长寿命`parent`的常见情况. -ren.亚历克西斯的解决方案更适用于多个长寿命的"孩子""拥有"一个短命的"父母"的边缘情况,我很少在野外看到它. (4认同)
  • 有很多案例可以让孩子和父母中的任何一个或两个人在没有其他人的情况下活着,只要其他实体尚未引用他们的话.当你可能想要使用相互弱的引用时(那些其他外部引用将完成所需的工作,只要需要). (2认同)