2017-06-26 146 views
6

在遍历它时修改字典。 Python中的错误字典?

d = {1: 1} 
for k in d.keys(): 
    d['{}'.format(k)] = d.pop(k) 
print(d) 

输出是{'1': 1}

d = {1: 1} 
for k in d.keys(): 
    d['i{}'.format(k)] = d.pop(k) 
print(d) 

{'iiiii1': 1}。这是一个错误?我正在运行Python 3.6.1 :: Anaconda 4.4.0 (x86_64)

+9

为什么会是一个错误?您在迭代*时删除并添加键*。你很幸运,你并没有陷入无限循环。 –

+3

绝不*改变*集合,而*重复*。特别是不是词典,因为它会导致怪异的行为。 –

+0

@ user2725109:你怎么知道第一个循环只运行一次?对于所有你知道它跑了1000次。 –

回答

13

不,这不是一个错误。这其实是explicitly documented

键和值指的是这个非随机的,不同的Python实现不同而不同,取决于插入和删除的字典的历史以任意顺序遍历。如果按键,值和项目视图被重复执行而不对词典进行中间修改,则项目顺序将直接对应。而添加或删除字典中的条目可能引发RuntimeError或无法遍历所有条目

[...]

迭代意见。

大胆强调我的。

您正在遍历键,同时添加和删除词典中的条目。这工作了几次迭代,然后你击中了一个未能遍历所有条目案件和迭代停止。

会发生什么情况是您在6次添加时触发重新大小,并导致迭代在此时失败; “下一个”键现在插入“更早”的插槽中。发生这种情况的测试,你只是不知道它在这两种情况下迭代5次:

>>> d = {1: 1} 
>>> for i, k in enumerate(d): 
...  print(i) 
...  d['{}'.format(k)] = d.pop(k) 
... 
0 
1 
2 
3 
4 
>>> d = {1: 1} 
>>> for i, k in enumerate(d): 
...  print(i) 
...  d['i{}'.format(k)] = d.pop(k) 
... 
0 
1 
2 
3 
4 

它运行,因为当前dict实现了hash table of size 8开始的5倍,并调整大小触发when the table is 2/3s full (您最初的dict有1项,5个插入使其> (8 * 2/3 == 5.333333)该表是越来越充满DKIX_DUMMY entities,进入时,删除键(正确处理散列冲突需要)。

注意,这是所有高度依赖于实现的。在Python 3.5和之前的版本中,这两个代码片段只是迭代而已一次(即使使用for k in d:来避免为键创建列表对象);迭代在3.6中继续,因为实现发生了变化,现在迭代遵循插入顺序。未来的Python版本可以自由地再次改变实现。