WeakValueDictionary retaining reference to object

2019-06-22 15:32发布

>>> from weakref import WeakValueDictionary
>>> class Foo(object):
...     pass
>>> foo = Foo()
>>> db = WeakValueDictionary()
>>> db['foo-id'] = foo
>>> del foo
>>> dict(db)
{'foo-id': <__main__.Foo object at 0x4dd946c>}

Why does it show this instead of an empty dictionary? Note that this code produces the result I'd expect:

>>> db2 = WeakValueDictionary()
>>> db2['disposable-id'] = Foo()
>>> dict(db2)
{}

It also behaves as expected when executing a script (instead of the interactive interpreter):

from weakref import WeakValueDictionary
class Foo(object):
    pass
foo = Foo()
db = WeakValueDictionary()
db['foo-id'] = foo
del foo
print str(dict(foo))
# prints {}

3条回答
Explosion°爆炸
2楼-- · 2019-06-22 15:40

In Python Interactive Shell, whatever variable you declare, is not garbage collected automatically, since the scope in __main__ has not ended. Garbage collection in Python is based on reference count, and until you do it explicitly or get off the current scope, it won't be collected.

In same shell, if you implement your WeakRef logic inside function, you will see the expected result, since upon finishing function, controls gets out of scope and objects are collected by garbage.

That's why example by @jdi with function is showing you exactly what you want.

查看更多
Explosion°爆炸
3楼-- · 2019-06-22 15:46

If you have only been trying this in an interactive shell, I believe it has to do with the way the garbage collection is working under that interface and the global scope operations.

Try this from a script:

foo.py

from weakref import WeakValueDictionary

class Foo(object):
    pass

f = Foo()
d = WeakValueDictionary()
d['f'] = f 

print dict(d)
del f 
print dict(d)

And then...

$ python foo.py

{'f': <__main__.Foo object at 0x101f496d0>}
{}

Now, try this from an interactive python shell, moving the operation under a functions scope:

from weakref import WeakValueDictionary

class Foo(object):
    pass

f = Foo()
d = WeakValueDictionary()
d['f'] = f 

def main():
    global f
    print dict(d)
    del f 
    print dict(d)

main()

#{'f': <__main__.Foo object at 0x100479f10>}
#{}
查看更多
Lonely孤独者°
4楼-- · 2019-06-22 15:51

WeakValueDictionary does not guarantee that entries will be removed when there are no normal references. What it guarantees is that it will not prevent garbage collection in due course - your object is garbage collectable, not garbage collected. The entry will disappear when garbage collection happens.

查看更多
登录 后发表回答