由于dict.keys
(以及dict.items
和dict.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
有谁知道为什么会这样?
答案 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原始字典由再次,使对象远离集合。