57

クラスのインスタンスが相互に親 <-> 子参照を持つコードがいくつかあります。たとえば、次のようになります。

class Node:

    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() の完了後に解放されないと思いますよね?. では、どうすれば解放されるのでしょうか。、または-のようなことができると思いますが、いつそれを行うべきかわからない場合はどうすればよいですか?rootc1c2root.children.clear()self.parent = None

これはweakrefモジュールを使用する適切な時期ですか? 正確には、何を弱参照化しますか? 属性parent?属性children?オブジェクト全体?上記のすべて?WeakKeyDictionary と weakref.proxy についての話が見られますが、この場合、それらをどのように使用する必要があるかは明確ではありません。

これは Python 2.4 でもあります (アップグレードできません)。

更新: 例とまとめ

どのオブジェクトを弱参照化するかは、どのオブジェクトが他のオブジェクトなしで存続できるか、およびどのオブジェクトが相互に依存しているかによって異なります。寿命が最も長いオブジェクトには、寿命が短いオブジェクトへの弱参照が含まれている必要があります。同様に、weakref を依存関係に作成するべきではありません。作成すると、依存関係がまだ必要であるにもかかわらず、黙って消えてしまう可能性があります。

たとえば、ツリー構造 があり、root子があり、子がなくkidsても存在できる場合、オブジェクトはその にweakrefsを使用する必要があります。これは、子オブジェクトが親オブジェクトの存在に依存する場合にも当てはまります。以下では、子オブジェクトはその深さを計算するために親を必要とするため、 の強力な参照です。ただし、属性のメンバーはオプションであるため、循環参照を防ぐために弱い参照が使用されます。rootkidsparentkids

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()

関係をひっくり返すために、以下のようなものがあります。ここで、FacadeクラスはSubsystem機能するためにインスタンスを必要とするため、必要なサブシステムへの強力な参照を使用します。 Subsystemただし、s が機能する必要はありませんFacade。 s は、互いのアクションについて sSubsystemに通知する方法を提供するだけです。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()
cli = Facade(sub)
4

3 に答える 3

33

ええ、ここではweakrefが優れています。具体的には、次の代わりに:

self.children = {}

使用する:

self.children = weakref.WeakValueDictionary()

コードを変更する必要は他にありません。このように、子に他に違いがない場合、それはそのまま消えchildrenます。その子を値として持つ親のマップ内のエントリも消えます。

weakref参照ループを回避することは、モジュールを使用する動機としてキャッシュを実装することと同等です。ref ループはあなたを殺すことはありませんが、特にメモリを詰まらせる可能性があります。インスタンスが含まれているクラスの一部が を定義している場合、それらのループを解消する のモジュール機能__del__が妨げられるためです。gc

于 2009-10-02T03:10:11.420 に答える
21

を使用することをお勧めしchild.parent = weakref.proxy(self)ます。parentこれは、 の有効期間が の有効期間をカバーする場合に循環参照を回避するための優れたソリューションですchild。の寿命が の寿命をカバーするself.children = weakref.WeakValueDictionary()場合に (Alex Martelli が提案したように)使用します。ただし、との両方が独立して有効な場合は、弱参照を使用しないでください。以下に、これらのルールを例で示します。childparentparentchild

ルートを名前にバインドして渡す場合は、弱参照の親を使用し、子はそこからアクセスします。

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

ルートがそれらからアクセスされている間に、各子を名前にバインドして渡す場合は、弱参照の子を使用します。

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
于 2009-10-02T08:17:00.477 に答える
1

どの参照が弱い可能性があるかを明確にしたかったのです。次のアプローチは一般的ですが、すべての例で二重リンク ツリーを使用します。

論理的ステップ 1。

必要な限りすべてのオブジェクトを存続させるために、強い参照があることを確認する必要があります。たとえば、次のように、さまざまな方法で実行できます。

  • [直接名]: ツリー内の各ノードへの名前付き参照
  • [コンテナ]: すべてのノードを格納するコンテナへの参照
  • [ルート + 子]: ルート ノードへの参照、および各ノードからその子への参照
  • [葉 + 親]: すべての葉ノードへの参照、および各ノードからその親への参照

論理的ステップ 2。

必要に応じて、情報を表す参照を追加します。

たとえば、ステップ 1 で [コンテナー] アプローチを使用した場合でも、エッジを表現する必要があります。ノード A と B の間のエッジは、単一の参照で表すことができます。どちらの方向にも進むことができます。繰り返しますが、多くのオプションがあります。たとえば、次のとおりです。

  • [children]: 各ノードからその子への参照
  • [parent]: 各ノードからその親への参照
  • [セットのセット]: 2 要素セットを含むセット。各 2 要素には、1 つのエッジのノードへの参照が含まれます

もちろん、ステップ 1 で [ルート + 子] アプローチを使用した場合は、すべての情報がすでに完全に表現されているため、このステップをスキップします。

論理的ステップ 3。

必要に応じて、参照を追加してパフォーマンスを向上させます。

たとえば、ステップ 1 で [container] アプローチを使用し、ステップ 2 で [children] アプローチを使用した場合、特定のアルゴリズムの速度を向上させ、各ノードとその親の間に参照を追加したい場合があります。このような情報は、(パフォーマンスを犠牲にして) 既存のデータから導出できるため、論理的に冗長です。


ステップ 1 のすべての参照は strong でなければなりません

ステップ 2 と 3 のすべての参照は、弱い場合と強い場合があります。強い参照を使用する利点はありません。循環が不可能であることがわかるまで、弱参照を使用することには利点があります。厳密に言えば、サイクルが不可能であることを知ったら、弱い参照を使用するか強い参照を使用するかに違いはありません。しかし、それについて考えないようにするために、ステップ 2 と 3 で弱い参照のみを使用することもできます。

于 2012-03-23T05:48:28.270 に答える