2015-10-07 42 views
3

我想了解的Python weakref模块及其使用情况保留GC.Collect的后值(),所以我有以下设置:的Python WeakValueDictionary在IPython中

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

它对我来说工作正常,你确定你没有继续引用'o1'或任何来自'ol'的任何其他名称的对象吗?重新启动python会话后尝试相同 –

+0

我使用'ipython 2.3.0'。所以我打开新的控制台和'%cpaste' my * setup *代码,然后复制/粘贴我的测试代码 - 输出结果在上面。没有其他变量也没有加载模块。 –

回答

1

的问题是,IPython中使用下列保持你的对象的引用 -

  1. _ - 最后一条语句的结果。

  2. __ - 第二条最后一条语句的结果。

  3. ___ - 最后一条语句的结果。

  4. In[num] - 结果/提示数num语句的输出 - 对于提示号num

  5. Out[num]运行该语句的字符串。

documentation -

Input and output history are kept in variables called In and Out , keyed by the prompt numbers, e.g. In[4] . The last three objects in output history are also kept in variables named _ , __ and ___ .

实际上,你可以尝试运行Out[2]看,这将是在参考了wdict1.items()(如果2提示号码,你跑了声明的结果,你给在你的例子中)。

IPython很可能会保留很多此类引用到您的对象,因此当您删除ol[0]o1o1之一的名称,然后执行gc.collect。它实际上并没有收集对象,因为仍然有对象的引用(在______Out[num])。

两个解决方案,我能想到的 -

  1. 使用%xdel魔法命令删除引用,就像%xdel ol[0],而不是del ol[0]。这会导致IPython清除它保留的所有引用。基于documentation -

Delete a variable, trying to clear it from anywhere that IPython’s machinery has references to it.

  • 你可以尝试运行这个测试的脚本,而不是交互,这些额外的引用不会产生这种情况下,你应该看到正确的行为。
  • +0

    你是对的,它与'ipython'有关......我只是在普通的'python'解释器上尝试了相同的代码,它工作得很好。谢谢。然而,你的'%xdel'解决方法不能很好地处理列表,给我一个错误:'NameError:name'ol [0]'没有被定义。对此有何想法? –

    +0

    是的,我想它不适用于列表对象。但是你真正的用例还需要你交互地运行吗? –

    +0

    不,没有真正的交互性,我只是喜欢尝试互动的新事物,我发现'ipython'是一个很好的工具。到目前为止,我发现的最佳解决方法是通过'ipython'中的'execfile()'运行脚本文件,然后按预期更新变量。 –