我正在尝试实施符合以下原则的内容:
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
实例已获取GC并从WeakValueDictionary
中删除。
我有什么方法可以达到这样的目的,以防止Container
和Thing
之间的循环引用?
编辑:如果我更改上面的代码,是否会解决循环引用问题?
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 :(得分:2)
WeakValueDictionary
的重点是,一旦对象不再使用,它的键会自动删除。
之后
self.dic[thing] = Thing(self)
不再引用Thing
以外的WeakValueDictionary
对象,因此您看到的行为是正确且可预期的。
如果您希望密钥可以访问,请将WeakValueDictionary
替换为常规dict
。或者,确保存在对该事物的引用,例如通过返回它或在其他地方引用它。
答案 1 :(得分:1)
您无需担心循环引用。在这种情况下,Python完全能够管理自己的内存。并且会在必要时删除带有循环引用的对象。
您的实施只需要这样:
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