我删除了我的字典,但我的dict_keys不介意,为什么会这样?

时间:2017-02-10 14:19:50

标签: python python-3.x dictionary

由于dict.keys(以及dict.itemsdict.values相似)返回字典对象的视图,我会假设删除它们生成的字典实际上对dict_keys我们是从他们那里得到的。

使用简单的字典和它的键:

d = {i:i for i in range(20)}
k = d.keys()  # similarly with d.values and d.items

显然,情况并非如此:

del d
i = iter(k)
list(i) # works fine and prints the keys

有谁知道为什么会这样?

2 个答案:

答案 0 :(得分:9)

del d会删除变量 d,但如果有其他引用,则它引用的对象将继续存在。您不需要字典视图来观察:

>>> d = {i:i for i in range(5)}
>>> dd = d
>>> d['x'] = 'x'
>>> del d
>>> d
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'd' is not defined
>>> dd
{0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 'x': 'x'}

d['x']='x'行表明dd=d 复制字典本身,它只是另外一个参考。)

还对比clear的行为,它修改了实际的对象:

>>> d = {i:i for i in range(5)}
>>> k = d.keys()
>>> del d
>>> list(k)
[0, 1, 2, 3, 4]

>>> d = {i:i for i in range(5)}
>>> k = d.keys()
>>> d.clear()
>>> list(k)
[]

答案 1 :(得分:4)

在对象上调用d.keys()只会增加字典对象的引用计数; del d不会触发标记为d的对象的垃圾收集器,因为引用始终大于零。

使用d.keys()调用sys.getrefcount时,您可以看到字典对象的引用计数增加:

from sys import getrefcount
d = {i:i for i in range(20)}
getrefcount(d) # 2
k = d.keys() 
getrefcount(d) # 3 (+1 due to d.keys())

在构造字典视图的调用中reference increment can be seen

Py_INCREF(dict);
dv->dv_dict = (PyDictObject *)dict;

在对象存储在视图对象struct的相应条目之前就完成了。

由于视图实际上只是一个带有对底层字典的引用的可迭代对象,因此您可以更深入一步并在获取其迭代器后删除键对象,并且仍然能够获取值:

i = iter(k) # +1 reference
del k, d
next(i)     # 0

iter(k) increases the reference count原始字典由再次,使对象远离集合。