更改正在迭代的对象从来都不是一个好主意。通常情况下dict
当您尝试时甚至会抛出异常:
name_dict = {1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6}
for k, v in name_dict.items():
name_dict.pop(k)
RuntimeError:字典在迭代期间更改了大小
但是,在您的情况下,您为每个删除的项目添加一个项目。这使得事情变得更加复杂。要理解发生了什么,您需要知道字典有点像稀疏表。例如像这样的字典{1: 1, 3: 3, 5: 5}
可能看起来像这样(这在 Python 3.6 中发生了变化,对于 3.6 及更高版本,以下内容不再正确):
hash key value
- - -
1 1 1
- - -
3 3 3
- - -
5 5 5
- - -
- - -
- - -
这也是迭代的顺序。因此,在第一次迭代中,它将转到第二项(其中1: 1
被储存了)。假设您将密钥更改为2
并拔掉钥匙1
该字典看起来像这样:
hash key value
- - -
- - -
2 2 1
3 3 3
- - -
5 5 5
- - -
- - -
- - -
但我们仍然在第二行,所以下一次迭代它将转到下一个“非空”条目,即2: 1
。哎呀...
使用字符串作为键会更加复杂,因为字符串哈希是随机的(基于每个会话),因此字典内的顺序是不可预测的。
在 3.6 中,内部布局发生了一些变化,但这里发生了类似的情况。
假设你有这个循环:
name_dict = {1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6}
for k, v in name_dict.items():
# print(k, k+6, name_dict.__sizeof__())
name_dict[k+6] = name_dict.pop(k)
# print(name_dict)
最初的布局是这样的:
key value
1 1
2 2
3 3
4 4
5 5
6 1
第一个循环删除1
但补充说7
。因为字典在 3.6 中是排序的,所以会在其中插入一个占位符1
曾经是:
key value
- -
2 2
3 3
4 4
5 5
6 1
7 2
这会一直持续到您更换为止4
with 10
.
key value
- -
- -
- -
- -
5 5
6 1
7 2
8 3
9 4
10 5
但是当你更换5
with 11
字典需要增加它的大小。然后发生了一些特别的事情:占位符被删除:
key value
6 6
7 1
8 2
9 3
10 4
11 5
所以,我们在上次迭代中处于位置 5,现在我们更改第 6 行。但是第 6 行包含11: 5
现在。哎呀...
切勿更改要迭代的对象:迭代期间不要弄乱键(值没问题)!
您可以保留一个“翻译表”(不知道这是否违反您的“不创建新字典”要求,但您需要某种存储来使代码正常工作)并在循环后进行重命名:
translate = {}
for k, v in name_dict.items():
print("This is the key: '%s' and this is the value '%s'\n" % (k, v) )
new_key = input("Please enter a new key: ")
translate[k] = new_key
time.sleep(4)
for old, new in translate.items():
name_dict[new] = name_dict.pop(old)