Python WeakValueDictionary 在 IPython 中保留 gc.collect() 之后的值
Python WeakValueDictionary retains values after gc.collect() in IPython
我想了解 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'
我的代码有什么问题,为什么所有的弱引用值都被保留了?
问题是 IPython 使用以下 -
保留对您的对象的引用
_
- 最后一条语句的结果。
__
- 倒数第二个语句的结果。
___
- 倒数第三个语句的结果。
In[num]
- 运行 提示编号 num
的语句的字符串
Out[num]
-提示号num
语句的result/output。
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 ___
.
您实际上可以尝试 运行ning Out[2]
并查看它是对 wdict1.items()
结果的引用(如果在 2
提示编号中您 运行 你在例子中给出的陈述)。
IPython 很可能保留了很多对您的对象的此类引用,因此当您删除 ol[0]
或 o1
等名称之一,然后执行 gc.collect
。它实际上并不收集对象,因为仍然有对该对象的引用(在 _
或 __
或 ___
或 Out[num]
中)。
我能想到的两个解决方案 -
- 使用
%xdel
magic command to remove the reference, like %xdel ol[0]
, instead of del ol[0]
. This would cause IPython to clear out all references it keeps as well. Based on documentation -
Delete a variable, trying to clear it from anywhere that IPython’s machinery has references to it.
- 您可以尝试 运行将此测试作为脚本而不是交互地进行,在这种情况下将不会创建这些额外的引用,您应该会看到正确的行为。
我想了解 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'
我的代码有什么问题,为什么所有的弱引用值都被保留了?
问题是 IPython 使用以下 -
保留对您的对象的引用_
- 最后一条语句的结果。__
- 倒数第二个语句的结果。___
- 倒数第三个语句的结果。In[num]
- 运行 提示编号num
的语句的字符串
Out[num]
-提示号num
语句的result/output。
Input and output history are kept in variables called
In
andOut
, keyed by the prompt numbers, e.g.In[4]
. The last three objects in output history are also kept in variables named_
,__
and___
.
您实际上可以尝试 运行ning Out[2]
并查看它是对 wdict1.items()
结果的引用(如果在 2
提示编号中您 运行 你在例子中给出的陈述)。
IPython 很可能保留了很多对您的对象的此类引用,因此当您删除 ol[0]
或 o1
等名称之一,然后执行 gc.collect
。它实际上并不收集对象,因为仍然有对该对象的引用(在 _
或 __
或 ___
或 Out[num]
中)。
我能想到的两个解决方案 -
- 使用
%xdel
magic command to remove the reference, like%xdel ol[0]
, instead ofdel ol[0]
. This would cause IPython to clear out all references it keeps as well. Based on documentation -
Delete a variable, trying to clear it from anywhere that IPython’s machinery has references to it.
- 您可以尝试 运行将此测试作为脚本而不是交互地进行,在这种情况下将不会创建这些额外的引用,您应该会看到正确的行为。