python中dict.keys()的奇怪行为

时间:2015-10-26 09:28:49

标签: python python-3.x

我的脚本中有这个代码来执行霍夫曼编码:

def huffmanEncoding(freqDict):
    for key in freqDict.keys():
        freqDict[HuffmanTree(value=key)] = freqDict.pop(key)
...

我想要做的是用一个树节点替换字典中的每个键,树节点的值是原始键。 HuffmanTree类正常工作。

然而,这段代码有很奇怪的行为。使用调试工具,我发现有时某些键被处理了两次或更多次,这意味着它们首先被转换为树节点,然后再次转换,使用其当前树节点作为新树节点的值。

我用下面显示的代码替换了我的代码:

def huffmanEncoding(freqDict):
    keys = list(freqDict.keys())
    for key in keys:
        freqDict[HuffmanTree(value=key)] = freqDict.pop(key)

现在它正常工作。但有人可以解释为什么我的第一个版本有这么奇怪的行为?如果我想更改字典中的所有键,我应该总是使用第二个版本吗?

2 个答案:

答案 0 :(得分:7)

您在迭代时向字典添加键和删除键。这意味着order of the keys also can change

通常情况下,Python会在执行此操作时引发异常,但由于每次迭代都要删除并添加一个键,因此引发该异常的内部检查无法检测到您在迭代时进行了更改。

迭代键视图的副本

def huffmanEncoding(freqDict):
    for key in list(freqDict):
        freqDict[HuffmanTree(value=key)] = freqDict.pop(key)

list()调用将所有键复制到单独的列表对象。而不是迭代live view of the dictionary keys,而是迭代静态不变的列表。从原始字典中弹出键也不会从列表副本中删除这些键,设置新键也不会导致列表获得更多键。这使循环完全稳定。

答案 1 :(得分:0)

第二个表单将从字典中获取所有键,并将其转换为列表。此列表未连接到字典,因此更改字典中的键不会更改该列表。

在第一种形式中,keys返回一个迭代器(它将动态生成值),因此更改字典可能会导致您在for循环中添加的键被keys迭代器返回

始终使用第二种形式。