2015-10-07 29 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()'運行腳本文件,然後按預期更新變量。 –