我正在尝试理解Python weakref
模块及其用例,因此我有以下设置:
import gc, weakref
class obj(object):
def __init__(self, val=None):
self._s = "Sample" if not val else " ".join(["Sample:", str(val)])
def sample(self):
return self._s
ol = [obj(x) for x in range(1,4)]
o1 = obj(1)
o2 = obj(2)
o3 = obj(3)
wdict1 = weakref.WeakValueDictionary({k:ol[k-1] for k in range(1,4)})
wdict2 = weakref.WeakValueDictionary()
wdict2[1] = o1
wdict2[2] = o2
wdict2[3] = o3
运行我的测试后,我可以清楚地看到,WeakValueDictionary
以某种方式保留了所有值,即使我已明确调用gc.collect()
。根据我的理解并按照以下answer中的说明,调用gc.collect()
应删除所有弱引用的值。
In [2]: wdict1.items()
Out[2]:
[(1, <__main__.obj at 0x7fea09c0be90>),
(2, <__main__.obj at 0x7fea09c0bf10>),
(3, <__main__.obj at 0x7fea09c0bf50>)]
In [3]: wdict2.items()
Out[3]:
[(1, <__main__.obj at 0x7fea09c51790>),
(2, <__main__.obj at 0x7fea09c0bed0>),
(3, <__main__.obj at 0x7fea09c0bf90>)]
In [4]: del ol[0]
In [5]: del o1
In [6]: gc.collect()
Out[6]: 64
In [7]: wdict1.items()
Out[7]:
[(1, <__main__.obj at 0x7fea09c0be90>),
(2, <__main__.obj at 0x7fea09c0bf10>),
(3, <__main__.obj at 0x7fea09c0bf50>)]
In [8]: wdict2.items()
Out[8]:
[(1, <__main__.obj at 0x7fea09c51790>),
(2, <__main__.obj at 0x7fea09c0bed0>),
(3, <__main__.obj at 0x7fea09c0bf90>)]
In [9]: del ol[0]
In [10]: del o2
In [11]: gc.collect()
Out[11]: 0
In [12]: wdict1.items()
Out[12]:
[(1, <__main__.obj at 0x7fea09c0be90>),
(2, <__main__.obj at 0x7fea09c0bf10>),
(3, <__main__.obj at 0x7fea09c0bf50>)]
In [13]: wdict2.items()
Out[13]:
[(1, <__main__.obj at 0x7fea09c51790>),
(2, <__main__.obj at 0x7fea09c0bed0>),
(3, <__main__.obj at 0x7fea09c0bf90>)]
In [14]: weakref.getweakrefs(ol[0])
Out[14]: [<weakref at 0x7fea0ab05470; to 'obj' at 0x7fea09c0bf50>]
In [15]: weakref.getweakrefs(o3)
Out[15]: [<weakref at 0x7fea09c060b0; to 'obj' at 0x7fea09c0bf90>]
In [16]: wdict1[1].sample()
Out[16]: 'Sample: 1'
In [17]: wdict2[2].sample()
Out[17]: 'Sample: 2'
我的代码出了什么问题,为什么保留所有弱引用的值?
答案 0 :(得分:1)
问题是IPython使用以下内容保持对对象的引用 -
_
- 最后一次陈述的结果。
__
- 最后一句话的结果。
___
- 最后一句话的结果。
In[num]
- 为提示编号num
Out[num]
- 提示编号num
声明的结果/输出。
来自documentation -
输入和输出历史记录保存在名为
In
和Out
的变量中,由提示编号键入,例如In[4]
。输出历史记录中的最后三个对象也保存在名为_
,__
和___
的变量中。
您实际上可以尝试运行Out[2]
并查看它是wdict1.items()
结果的引用(如果在2
提示号码中您运行了该语句,就像您在例)。
IPython最有可能保留对您的对象的大量此类引用,因此当您删除其中一个名称(如ol[0]
或o1
),然后执行gc.collect
时。它实际上并不收集对象,因为仍有对象的引用(在_
或__
或___
或Out[num]
)。
我能想到的两个解决方案 -
%xdel
magic命令删除引用,例如%xdel ol[0]
,而不是del ol[0]
。这将导致IPython清除它保留的所有引用。基于documentation - 删除一个变量,尝试从IPython机器引用它的任何地方清除它。