如何以及何时在 Python 中适当地使用weakref
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1507566/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How and when to appropriately use weakref in Python
提问by Richard Levasseur
I have some code where instances of classes have parent<->child references to each other, e.g.:
我有一些代码,其中类的实例具有相互的父<->子引用,例如:
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()
I thinkthis creates circular references such that root
, c1
and c2
won't be freed after Run() is completed, right?. So, how do get them to be freed? I think I can do something like root.children.clear()
, or self.parent = None
- but what if I don't know when to do that?
我认为这会创建循环引用root
,c1
并且c2
在 Run() 完成后不会被释放,对吧?那么,如何让他们获得自由呢?我想我可以做类似的事情root.children.clear()
,或者self.parent = None
- 但如果我不知道什么时候做呢?
Is this an appropriate time to use the weakref module? What, exactly, do I weakref'ify? the parent
attribute? The children
attribute? The whole object? All of the above? I see talk about the WeakKeyDictionary and weakref.proxy, but its not clear to me how they should be used, if at all, in this case.
这是使用weakref 模块的合适时机吗?我到底弱化了什么?该parent
属性?该children
属性?整个对象?上述所有的?我看到有关 WeakKeyDictionary 和 weakref.proxy 的讨论,但我不清楚在这种情况下应该如何使用它们(如果有的话)。
This is also on python2.4 (can't upgrade).
这也是在python2.4上(无法升级)。
Update: Example and Summary
更新:示例和摘要
What objects to weakref-ify depends on which object can live without the other, and what objects depend on each other. The object that lives the longest should contain weakrefs to the shorter-lived objects. Similarly, weakrefs should not be made to dependencies - if they are, the dependency could silently disappear even though it is still needed.
哪些对象要弱引用取决于哪些对象可以在没有其他对象的情况下生存,以及哪些对象相互依赖。寿命最长的对象应该包含对寿命较短的对象的弱引用。类似地,不应将弱引用用于依赖项——如果是这样,即使仍然需要依赖项,它也可能会悄悄消失。
If, for example, you have a tree structure, root
, that has children, kids
, but can exist withoutchildren, then the root
object should use weakrefs for its kids
. This is also the case if the child object depends on the existence of the parent object. Below, the child object requiresa parent in order to compute its depth, hence the strong-ref for parent
. The members of the kids
attribute are optional, though, so weakrefs are used to prevent a circular reference.
例如,如果您有一个树结构 ,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.
To flip the relationship around, we have something like the below. Here, the Facade
classes require a Subsystem
instance to work, so they use a strong-ref to the subsystem they need. Subsystem
s, however, don't require a Facade
to work. Subsystem
s just provide a way to notify Facade
s about each other's actions.
为了翻转关系,我们有如下内容。在这里,Facade
类需要一个Subsystem
实例才能工作,因此它们使用对所需子系统的强引用。 Subsystem
s,但是,不需要Facade
工作。 Subsystem
s 只是提供一种方法来通知Facade
s 彼此的行为。
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
采纳答案by Alex Martelli
Yep, weakref's excellent here. Specifically, instead of:
是的,weakref 在这里非常棒。具体来说,而不是:
self.children = {}
use:
利用:
self.children = weakref.WeakValueDictionary()
Nothing else needs change in your code. This way, when a child has no other differences, it just goes away -- and so does the entry in the parent's children
map that has that child as the value.
您的代码中没有任何其他内容需要更改。这样,当孩子没有其他差异时,它就会消失——父children
映射中将该孩子作为值的条目也是如此。
Avoiding reference loops is up high on a par with implementing caches as a motivation for using the weakref
module. Ref loops won't kill you, but they may end up clogging your memory, esp. if some of the classes whose instances are involved in them define __del__
, since that interferes with the gc
's module ability to dissolve those loops.
避免引用循环与实现缓存作为使用该weakref
模块的动机相当。Ref 循环不会杀死您,但它们最终可能会堵塞您的记忆,尤其是。如果其中涉及实例的某些类定义了__del__
,因为这会干扰gc
模块解散这些循环的能力。
回答by Denis Otkidach
I suggest using child.parent = weakref.proxy(self)
. This is good solution to avoid circular references in case when lifetime of (external references to) parent
covers lifetime of child
. Contrary, use weakref
for child
(as Alex suggested) when lifetime of child
covers lifetime of parent
. But never use weakref
when both parent
and child
can be alive without other.
我建议使用child.parent = weakref.proxy(self)
. 这是一个很好的解决方案,可以在(外部引用)的parent
生命周期覆盖child
. 相反,使用weakref
了child
(亚历克斯建议)当一辈子child
的盖寿命parent
。但是weakref
当两者都可以并且没有其他人可以活着时parent
,永远不要使用child
。
Here these rules are illustrated with examples. Use weakref-ed parent if you store root in some variable and pass it around, while children are accessed from it:
此处通过示例说明了这些规则。如果将 root 存储在某个变量中并传递它,而从它访问子级,则使用弱引用父级:
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
Use weakref-ed children if you bind all them to variables, while root is accessed through them:
如果将所有子项都绑定到变量,则使用弱引用子项,而通过它们访问 root:
def Run():
root, c1, c2 = Node(), Node(), Node()
root.AddChild("first", c1)
root.AddChild("second", c2)
return c1, c2
But neither will work for the following:
但两者都不适用于以下情况:
def Run():
root, c1, c2 = Node(), Node(), Node()
root.AddChild("first", c1)
root.AddChild("second", c2)
return c1
回答by max
I wanted to clarify which references can be weak. The following approach is general, but I use the doubly-linked tree in all examples.
我想澄清哪些引用可能很弱。以下方法是通用的,但我在所有示例中都使用双向链接树。
Logical Step 1.
逻辑步骤 1。
You need to ensure that there are strong references to keep all the objects alive as long as you need them. It could be done in many ways, for example by:
您需要确保存在强引用以在需要时使所有对象保持活动状态。它可以通过多种方式完成,例如:
- [direct names]: a named reference to each node in the tree
- [container]: a reference to a container that stores all the nodes
- [root + children]: a reference to the root node, and references from each node to its children
- [leaves + parent]: references to all the leaf nodes, and references from each node to its parent
- [直接名称]:对树中每个节点的命名引用
- [container]:对存储所有节点的容器的引用
- [root + children]:对根节点的引用,以及每个节点对其子节点的引用
- [叶+父]:对所有叶节点的引用,以及从每个节点到其父节点的引用
Logical Step 2.
逻辑步骤 2。
Now you add references to represent information, if required.
现在,如果需要,您可以添加引用来表示信息。
For instance, if you used [container] approach in Step 1, you still have to represent the edges. An edge between nodes A and B can be represented with a single reference; it can go in either direction. Again, there are many options, for example:
例如,如果您在步骤 1 中使用 [容器] 方法,您仍然需要表示边缘。节点 A 和 B 之间的边可以用单个参考表示;它可以朝任何一个方向发展。同样,有很多选择,例如:
- [children]: references from each node to its children
- [parent]: a reference from each node to its parent
- [set of sets]: a set containing 2-element sets; each 2-element contains references to nodes of one edge
- [children]:从每个节点到其子节点的引用
- [parent]:从每个节点到其父节点的引用
- [set of sets]:包含2个元素的集合;每个 2 元素包含对一条边的节点的引用
Of course, if you used [root + children] approach in Step 1, all your information is already fully represented, so you skip this step.
当然,如果您在步骤 1 中使用 [root + children] 方法,则您的所有信息都已经完全表示,因此您可以跳过此步骤。
Logical Step 3.
逻辑步骤 3。
Now you add references to improve performance, if desired.
现在,如果需要,您可以添加引用以提高性能。
For instance, if you used [container] approach in Step 1, and [children] approach in Step 2, you might desire to improve the speed of certain algorithms, and add references between each each node and its parent. Such information is logically redundant, since you could (at a cost in performance) derive it from existing data.
例如,如果您在步骤 1 中使用 [container] 方法,在步骤 2 中使用 [children] 方法,您可能希望提高某些算法的速度,并在每个节点与其父节点之间添加引用。此类信息在逻辑上是冗余的,因为您可以(以性能为代价)从现有数据中获取它。
All the references in Step 1 must be strong.
步骤 1 中的所有引用都必须是强引用。
All the references in Steps 2 and 3 may be weak or strong. There is no advantage to using strong references. There is an advantage to using weak references until you know that cycles are no longer possible. Strictly speaking, once you know that cycles are impossible, it makes no difference whether to use weak or strong references. But to avoid thinking about it, you might as well use exclusively weak references in the Steps 2 and 3.
第 2 步和第 3 步中的所有引用都可能是弱引用或强引用。使用强引用没有任何好处。在您知道循环不再可能之前,使用弱引用是有好处的。严格来说,一旦你知道循环是不可能的,那么使用弱引用还是强引用都没有区别。但是为了避免考虑,您不妨在步骤 2 和步骤 3 中专门使用弱引用。