クラスのインスタンスが相互に親<->子参照を持っているコードがいくつかあります。例:
_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()
_
Ithinkこれにより、root
、_c1
_、および_c2
_が後で解放されないような循環参照が作成されますRun()が完了しましたね。では、どうすればそれらを解放することができますか? root.children.clear()
や_self.parent = None
_のようなことができると思いますが、いつそれを行うべきかわからない場合はどうなりますか?
これはweakrefモジュールを使用するのに適切な時期ですか?正確には、私は何を弱くしますか? parent
属性? children
属性?オブジェクト全体?上記のすべて? WeakKeyDictionaryとweakref.proxyについての話はありますが、この場合、それらをどのように使用する必要があるかはわかりません。
これはpython2.4にもあります(アップグレードできません)。
更新:例と要約
どのオブジェクトをweakref-ifyするかは、どのオブジェクトが他のオブジェクトなしで生きることができるか、およびどのオブジェクトが互いに依存しているかによって異なります。最も寿命の長いオブジェクトには、寿命の短いオブジェクトへのweakrefが含まれている必要があります。同様に、依存関係に対してweakrefを作成しないでください。そうすると、依存関係がまだ必要であっても、依存関係が黙って消えてしまう可能性があります。
たとえば、子root
を持つツリー構造kids
がありますが、なしで存在できる場合)子の場合、root
オブジェクトはそのkids
にweakrefsを使用する必要があります。これは、子オブジェクトが親オブジェクトの存在に依存している場合にも当てはまります。以下では、子オブジェクトは、その深さを計算するために親を必要とします。したがって、parent
のstrong-refです。ただし、kids
属性のメンバーはオプションであるため、循環参照を防ぐためにweakrefが使用されます。
_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
インスタンスが必要なので、必要なサブシステムへのstrong-refを使用します。ただし、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
_
うん、weakrefはここで素晴らしい。具体的には、次の代わりに:
self.children = {}
使用する:
self.children = weakref.WeakValueDictionary()
コードを変更する必要は他にありません。このように、子に他の違いがない場合、子は消えるだけです。その子を値として持つ親のchildren
マップのエントリも消えます。
参照ループを回避することは、weakref
モジュールを使用する動機としてキャッシュを実装することと同等です。 Refループはあなたを殺すことはありませんが、特にあなたの記憶を詰まらせる可能性があります。インスタンスがそれらに関与しているクラスのいくつかが__del__
を定義している場合、それはそれらのループを解消するgc
のモジュール機能を妨げるためです。
child.parent = weakref.proxy(self)
を使用することをお勧めします。これは、(外部参照)parent
の有効期間がchild
の有効期間をカバーする場合に循環参照を回避するための優れたソリューションです。逆に、weakref
の有効期間がchild
の有効期間をカバーする場合は、(Alexが提案したように)child
にparent
を使用します。ただし、weakref
とparent
の両方が他なしで存続できる場合は、child
を使用しないでください。
ここでは、これらのルールを例とともに示します。 rootを変数に格納して渡し、子にアクセスする場合は、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
Rootにアクセスするときに、すべての子を変数にバインドする場合は、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
どの参照が弱い可能性があるかを明確にしたかったのです。次のアプローチは一般的ですが、すべての例で二重にリンクされたツリーを使用します。
論理ステップ1。
必要な限りすべてのオブジェクトを存続させるには、強力な参照があることを確認する必要があります。これは、次のようなさまざまな方法で実行できます。
論理ステップ2。
次に、必要に応じて、情報を表す参照を追加します。
たとえば、手順1で[コンテナ]アプローチを使用した場合でも、エッジを表す必要があります。ノードAとノードBの間のエッジは、単一の参照で表すことができます。それはどちらの方向にも行くことができます。繰り返しますが、たとえば次のような多くのオプションがあります。
もちろん、ステップ1で[root + children]アプローチを使用した場合、すべての情報はすでに完全に表現されているため、このステップはスキップします。
論理ステップ3。
次に、必要に応じて、パフォーマンスを向上させるための参照を追加します。
たとえば、ステップ1で[container]アプローチを使用し、ステップ2で[children]アプローチを使用した場合、特定のアルゴリズムの速度を向上させ、各ノードとその親の間に参照を追加したい場合があります。このような情報は、(パフォーマンスを犠牲にして)既存のデータから取得できるため、論理的に冗長です。
ステップ1のすべての参照は強力である必要があります。
ステップ2と3のすべての参照は弱いか強いかもしれません。強力な参照を使用する利点はありません。サイクルが不可能であることがわかるまで、弱参照を使用することには利点があります。厳密に言えば、サイクルが不可能であることがわかったら、弱い参照を使用するか強い参照を使用するかは関係ありません。ただし、それについて考えるのを避けるために、ステップ2と3では弱い参照のみを使用することをお勧めします。