2016-05-05 4 views
1

私は以下の原理で動作何かを実装しようとしています:2つのオブジェクト間の循環参照にweakrefを使用する必要がありますか?

from weakref import WeakValueDictionary 

class Container(object): 
    def __init__(self): 
     self.dic = WeakValueDictionary({}) 
    def put_in(self, something): 
     self.dic[something] = Thing(self, something) 

class Thing(object): 
    def __init__(self, container, name): 
     self.container = container 
     self.name = name 

    def what_I_am(self): 
     print("I am a thing called {}".format(self.name)) 

pot = Container() 
pot.put_in('foo') 
pot.dic['foo'].what_I_am() 

しかし、私が手を:

File "C:/Users/jacques/ownCloud/dev/weakref.py", line 26, in <module> 
    pot.dic['foo'].what_I_am() 
    File "C:\Program Files\Anaconda3\lib\weakref.py", line 131, in __getitem__ 
    o = self.data[key]() 
KeyError: 'foo' 

私はThingインスタンスがGCedを取得し、から削除したため、私の実装が正しくないことを理解WeakValueDictionary

循環参照がContainerThingの間になるのを防ぐために、これを実現する方法はありますか?

編集:上記のコードを上記のものに変更した場合、循環参照の問題は解決しますか?

from weakref import proxy 

class Container(dict): 
    def put_in(self, something): 
     self[something] = Thing(self) 

class Thing(object): 
    def __init__(self, container): 
     self.container = proxy(container) 

    def what_is_it(self): 
     print("I am a thing called {}".format(self)) 

    def __getattr__(self, name): 
     try: #Look up the Thing instance first 
      return object.__getattribute__(self, name) 
     except AttributeError: #Try to find the attribute in container 
      return self.container.__getattribute__(name) 

    def __format__(self, spec): 
     (name,) = (key for key, val in self.container.items() if self == val) 
     return name 

pot = Container() 
pot.location = 'Living room' 
pot.put_in('foo') 
pot['foo'].what_is_it() 
print(pot['foo'].location) 
+0

'シング(自己)は' '自己*前*と呼ばれることに注意してください.dic [thing] = 'が実行されます。 – kennytm

+0

はい、私は通常のdictで問題があります。厚い卵のようなものです。しかし、私は 'dic [thing] = None'でこれを解決できます。 –

+0

" Thing'インスタンスはGCを取得する可能性があります。まあ、**はガベージコレクションされています。したがって、 'KeyError'。 –

答えて

1

循環参照を心配する必要はありません。この場合、Pythonは独自のメモリを完全に管理することができます。また、必要に応じて円形参照を持つオブジェクトを削除します。

あなたimplemenationはこれだけのように見える必要が

class Container(dict): 
    def put_in(self, something): 
     self[something] = Thing(self, something) 

class Thing: 
    def __init__(self, container, name): 
     self.container = container 
     self.name = name 

    def what_is_it(self): 
     assert self.container[self.name] is self, "Thing stored under wrong name" 
     print("I am a thing called {}".format(self.name)) 

    def __getattr__(self, name): 
     # By the time __getattr__ is called, normal attribute access on Thing has 
     # already failed. So, no need to check again. Go straight to checking the 
     # container 
     try: 
      return getattr(self.container, name) 
     except AttributeError: 
      # raise a fresh attribute error to make it clearer that the 
      # attribute was initially accessed on a Thing object 
      raise AttributeError("'Thing' object has no attribute {!r}".format(name)) from e 

物事がどのように動作するかをお見せするために簡単なテスト:

c = Container() 
c.put_in("test") 
c.value = 0 

# Attribute demonstration 
c["test"].what_is_it() 
t = c["test"] 
print("name:", t.name) # get a Thing attribute 
print("value:", t.value) # get a Container Attribute 
c.name = "another name" 
print("Thing name:" t.name) # gets Thing attrs in preference to Container attrs 

# Garbage collection demonstration 
import weakref 
import gc 

r = weakref.ref(c["test"]) 
del c, t 
# no non-weak references to t exist anymore 
print(r()) # but Thing object not deleted yet 
# collecting circular references is non-trivial so Python does this infrequently 

gc.collect() # force a collection 
print(r()) # Thing object has now been deleted 
+0

'__getattr__'についてのヒントをありがとう! –

+0

行 'except AttributeError'は' as e 'ビットを欠いています。 –

+0

例外は使用されていないため、at 'as e'ビットは不要です。したがって、ローカル変数にバインドする必要はありません。 – Dunes

2

WeakValueDictionaryの非常にポイントは、オブジェクトが使用されなくなっている一度そのキーは自動的に削除されていないということです。

すぐ

self.dic[thing] = Thing(self) 

後にもうWeakValueDictionaryの外Thingオブジェクトへの参照はありませんので、あなたが見ている行動は正しいと期待されています。

キーにアクセスできると思われる場合は、WeakValueDictionaryを通常のdictに置き換えます。別の方法として、そのオブジェクトへの参照があることを確認してください。たとえば、それを返すか、別の場所を参照するなどです。

+0

@ KevinJ.Chaseありがとう、固定。私はそれが「Thing」クラスではないことを明確にしたいと思っていましたが、誤って混乱を招いたようです。 – phihag

関連する問題