【问题标题】:Python WeakValueDictionary retains values after gc.collect() in IPythonPython WeakValueDictionary 在 IPython 中的 gc.collect() 之后保留值
【发布时间】:2015-10-07 09:58:49
【问题描述】:

我正在尝试了解 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'

我的代码有什么问题,为什么保留了所有弱引用的值?

【问题讨论】:

  • 它对我来说很好用,你确定你没有引用o1ol 中的任何其他名称的对象吗?重新启动python会话后尝试相同
  • 我使用ipython 2.3.0。所以我打开新的控制台和%cpaste 我的setup 代码,然后复制/粘贴我的测试代码——输出在上面。没有加载其他变量或模块。

标签: python python-2.7 garbage-collection ipython


【解决方案1】:

问题是 IPython 使用以下方式保持对您的对象的引用 -

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

  2. __ - 倒数第二个语句的结果。

  3. ___ - 倒数第三个语句的结果。

  4. In[num] - 为提示号 num 运行的语句字符串

  5. Out[num] - 提示号num 语句的结果/输出。

来自documentation-

输入和输出历史记录保存在名为 InOut 的变量中,由提示数字键入,例如In[4]。输出历史中的最后三个对象也保存在名为 ______ 的变量中。

您实际上可以尝试运行Out[2] 并看到它是对wdict1.items() 结果的引用(如果您在2 提示号中运行了该语句,就像您在示例中给出的那样)。

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

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

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

删除一个变量,试图从任何 IPython 机器引用它的地方清除它。

  1. 您可以尝试将此测试作为脚本运行,而不是以交互方式运行,在这种情况下,不会创建这些额外的引用,并且您应该会看到正确的行为。

【讨论】:

  • 你说得对,它与ipython... 我刚刚在普通的python 解释器上尝试了相同的代码,它工作得很好。谢谢。但是,您的 %xdel 解决方法不适用于列表,给我一个错误:NameError: name 'ol[0]' is not defined。对此有什么想法吗?
  • 是的,我猜它不适用于列表对象。但是您的真实用例是否也需要您以交互方式运行它?
  • 不,不是真正的交互式,我只是喜欢尝试交互式新事物,我发现'ipython' 是一个非常好的工具。到目前为止,我发现的最佳解决方法是通过 execfile()ipython 中运行脚本文件,然后按预期更新变量。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-23
  • 2012-04-05
  • 1970-01-01
  • 2012-04-17
  • 1970-01-01
  • 2011-05-13
  • 2012-08-14
相关资源
最近更新 更多