Python 如何使用d.items()更改for循环中的所有字典键?

Python 如何使用d.items()更改for循环中的所有字典键?,python,python-3.x,dictionary,for-loop,Python,Python 3.x,Dictionary,For Loop,我想要一些帮助来理解为什么这段代码不能像预期的那样工作 如果要更改字典的键但保留值,可以使用: d[new_key] = d.pop[old_key] 我想修改所有键(并保持值不变),但下面的代码跳过了某些行(“col2”)保持不变。是不是因为字典是无序的,而我一直在更改字典中的值 在不创建新字典的情况下,如何更改键并保留值 import time import pprint name_dict = {"col1": 973, "col2": "1452 29th Street",

我想要一些帮助来理解为什么这段代码不能像预期的那样工作

如果要更改字典的键但保留值,可以使用:

d[new_key] = d.pop[old_key]
我想修改所有键(并保持值不变),但下面的代码跳过了某些行(“col2”)保持不变。是不是因为字典是无序的,而我一直在更改字典中的值

在不创建新字典的情况下,如何更改键并保留值

import time
import pprint

name_dict = {"col1": 973, "col2": "1452 29th Street",
             "col3": "Here is a value", "col4" : "Here is another value",
             "col5" : "NULL", "col6": "Scottsdale",
             "col7": "N/A", "col8" : "41.5946922",
             "col9": "Building", "col10" : "Commercial"}


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: ")
    name_dict[new_key] = name_dict.pop(k)
    time.sleep(4)

pprint.pprint(name_dict)

更改正在迭代的对象从来都不是一个好主意。通常情况下,
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和更新版本,以下内容不再正确):

这也是迭代的顺序。因此,在第一次迭代中,它将转到第二项(存储
1:1
)。让我们假设您将键更改为
2
,然后移除键
1
,dict将如下所示:

hash    key    value
   -      -        - 
   -      -        - 
   2      2        1
   3      3        3
   -      -        - 
   5      5        5
   -      -        - 
   -      -        - 
   -      -        - 
key   value
  1       1
  2       2
  3       3
  4       4
  5       5
  6       1
但是我们仍然在第二行,所以下一次迭代将转到下一个“notempty”条目,即
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)
初始布局如下所示:

hash    key    value
   -      -        - 
   -      -        - 
   2      2        1
   3      3        3
   -      -        - 
   5      5        5
   -      -        - 
   -      -        - 
   -      -        - 
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
替换为
10

key   value
  -       -
  -       -
  -       -
  -       -
  5       5
  6       1
  7       2
  8       3
  9       4
 10       5
但是,当您将
5
替换为
11
时,词典将需要增加其大小。然后发生了一些特殊情况:占位符被删除:

key   value
  6       6
  7       1
  8       2
  9       3
 10       4
 11       5
所以,我们在上一次迭代中处于位置5,现在我们更改第6行。但是第6行现在包含
11:5
。组

永远不要更改您正在迭代的对象:不要在迭代过程中弄乱键(值是可以的)! 您可以保留一个“翻译表”(不知道这是否违反了“不创建新dict”的要求,但您需要某种存储来确保代码正常工作),并在循环后进行重命名:

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)
在python3中,dict.items()只是dict上的一个视图。由于在迭代dict.items()时不允许修改iterable,因此在迭代dict.items()时不允许修改dict。 在迭代之前,必须将items()复制到列表中

for k, v in list(name_dict.items()):
    ...
    name_dict[new_key] = name_dict.pop(k)
这确实满足了您的“无新dict”要求,尽管该列表实际上包含您所有数据的完整副本

只需复制键,就可以稍微减少内存占用

for k in list(name_dict):
    v = name_dict.pop(k)
    ...
    name_dict[new_key] = v

编辑:由于Sven Krüger,他提出了一个旧钥匙与新钥匙碰撞问题的可能性。如果是那样的话,你就得走了

kv = list(name_dict.items())
name_dict.clear()
for k, v in kv :
    ...
    name_dict[new_key] = v

顺便说一下,有一个不创建新dict的用例,当前dict可能会在其他地方引用。

为了在工作内存中有一个不依赖于原始词典的iterable对象,您可以使用方法
fromkeys
。现在可以使用旧值指定新键。但是有一件事你必须记住:你不能给一个新键赋值,这个新键不是某个旧键,而这个新键也是旧键集中的另一个键

Old_Keys = { old_key_1, old_key_2, ..., old_key_n }
因此,将与旧键相关的值指定给新键

old_key_1  ->  new_key_1 not in Old_Keys  # Okay!
old_key_2  ->  new_key_2 == old_key_4     # Boom!... Error!...
使用以下工具时请注意这一点

代码

D = {'key1': 'val1', 'key2': 'val2', 'key3': 'val3'}

for key in D.fromkeys(D) :
    new_key = raw_input("Old Key: %s, New Key: " % key)
    D[new_key] = D.pop(key)

print D
Old Key: key1, New Key: abc

Old Key: key2, New Key: def

Old Key: key3, New Key: ghi

{"abc": 'val1', "def": 'val2', "ghi": 'val3'}
控制台

D = {'key1': 'val1', 'key2': 'val2', 'key3': 'val3'}

for key in D.fromkeys(D) :
    new_key = raw_input("Old Key: %s, New Key: " % key)
    D[new_key] = D.pop(key)

print D
Old Key: key1, New Key: abc

Old Key: key2, New Key: def

Old Key: key3, New Key: ghi

{"abc": 'val1', "def": 'val2', "ghi": 'val3'}

字典是无序的-完全可以这样做
new_dict={input('enternewkey for{})。格式(k)):v代表k,v在name_dict.items()中
@JonClements,在不创建新词典的情况下,在迭代过程中进行修改往往会导致意外行为,为什么不需要一个新的
dict
,内存是问题吗?@RomanPerekhrest啊,这很好。。。但还是。。。除非它是纯理论的或绝对的要求-毫无疑问这是最简单的…为什么不允许您在迭代时“允许”修改iterable?当您更改迭代的iterable时,它通常会产生(不必要的)副作用,但这并不意味着它是不允许的……我同意,从语言的角度来看,它是允许的。对我来说,从设计的角度来看,这是不允许的,除非你完全理解副作用,并且你是唯一的维护者:-)。我猜在python3程序员的错误统计中,这是一个得分最高的人。这是一个非常有趣的学习经历。非常感谢花时间帮助一位Pythonista同胞。你呈现情境的方式完美且易于理解。fromkeys实际上创建了一个新的dict,根据问题,这是不允许的。键冲突是一个很好的观点。我的理解是,生成的字典不允许是新变量。