Python中循环更新字典键

4

我想更新我的字典 c 的键值,使用新的键值 k_new。虽然我参考了不同的问题解答,例如 这个,但是它似乎没有被更新。请告诉我哪里出了问题。

from nltk.stem import WordNetLemmatizer
lemmatizer = WordNetLemmatizer()
c = {'apples': 3, 'biscuits and tea': 3, 'oranges and onions': 4}
for k in c:
    splits=k.split()
    k_new= " ".join(lemmatizer.lemmatize(w.lower()) for w in splits)
    c[k_new] = c.pop(k)
print(c)

PS:我也使用了:

c[k_new] = c[k]
del c[k]

我遇到了一个问题:RuntimeError: dictionary changed size during iteration

请帮忙解决。


1
不要在迭代字典时更新它... - Willem Van Onsem
1
从一个列表中移除项目并同时进行迭代的方法: - Jean-François Fabre
2个回答

3

当您迭代字典时,可以更新它:

from nltk.stem import WordNetLemmatizer
lemmatizer = WordNetLemmatizer()
c = {'apples': 3, 'biscuits and tea': 3, 'oranges and onions': 4}
for k in c:  # iterate over c
    splits=k.split()
    k_new= " ".join(lemmatizer.lemmatize(w.lower()) for w in splits)
    c[k_new] = c.pop(k)  # update (two times) c
print(c)

在迭代集合时更新它通常是一个非常糟糕的想法。大多数数据结构都不是为此设计的。

但是,您可以构建一个新字典:

from nltk.stem import WordNetLemmatizer
lemmatizer = WordNetLemmatizer()
c = {'apples': 3, 'biscuits and tea': 3, 'oranges and onions': 4}
<b>c_new = {}</b>
for k in c:
    splits=k.split()
    k_new= " ".join(lemmatizer.lemmatize(w.lower()) for w in splits)
    <b>c_new</b>[k_new] = <b>c[k]</b>
print(<b>c_new</b>)

我们可以使用字典推导式使这个过程更加优雅:
{" ".join(lemmatizer.lemmatize(w.lower()) for w in k.split()): v
 for k,v in c.items()}

这个一行代码构建了一个新的字典,我们遍历 c 的键值对k,v,并添加一个键" ".join(lemmatizer.lemmatize(w.lower()) for w in k.split()),该键与值v相关联。


@thebjorn:是的。这就是我在列表推导式中所做的:我提出了一个修复方案,但是旨在展示一种更优雅的方法 - 在我看来。 - Willem Van Onsem
你的两个解决方案都创建了新的字典,这样做是可以的,但在这种情况下并不是必需的,因为如果迭代c.items()(或创建任何其他单独的数据结构来保存键),OP可以直接就地修改他的字典。个人认为,在这种情况下,字典推导式过于复杂(而且: v几乎消失了),但这可能更多是一个口味问题。 - thebjorn
@thebjorn:但这样我们将使用内存来保存键。c.items()也是一个可迭代对象,因此这也不是一个解决方案(在Python-3.x中会产生相同的错误)。由于items()是一个代理,它几乎以相同的方式遍历字典,但同时还考虑了字典中的值。 - Willem Van Onsem
这是Py3的一个缺陷,在我的世界里,c.items()返回一个列表 :-D 顺便说一句:你在内存中持有两个完整的字典副本,所以c.items()(py2)或list(c)不会消耗更多的内存。 - thebjorn
@thebjorn: 那列表是如何维护的?这意味着使用malloc构造一个列表对象,其大小至少为引用指针大小乘以其中项数的大小。由于我们希望附加操作的分摊成本为*O(1)*,通常可以将其大小设置为下一个最大的二次幂。 - Willem Van Onsem

2

当字典在改变的时候进行迭代可能会产生奇怪的效果,因为你正在使用一个实时引用来访问键。只需将键视图转换成列表即可解决:

for k in list(c):
    ...

谢谢您的回答。然而,我想在更改键时也将值分配给键。我们可以使用列表来实现吗? - user8566323

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接