为什么在迭代`dict_keys`时密钥从子类dict中消失?

时间:2017-12-05 19:59:11

标签: python python-3.x dictionary key

当迭代从dict子类化的对象的键时,我在Python 3.6.3中遇到了奇怪的行为。我已经理解dict.keys()的行为从Python 2变为Python 3以返回dict_keys对象,因此看到的浅拷贝行为是可以理解的。我不明白为什么dict_keys中的一个元素可以通过显式搜索找到,但在迭代dict_keys对象时不会出现。

这是MWE:

#!/usr/bin/env python

from __future__ import print_function

class chemdist(dict):
    def normalize_names(self, force_static=False):

        if force_static:
            raw_chems = tuple(self.keys())
        else:
            raw_chems = self.keys()

        print("raw_chems is " + repr(raw_chems))

        if 'Ar' in raw_chems:
            print("Found Argon!")

        for old_chem in raw_chems:
            print("Checking {:s}".format(old_chem))
            if old_chem.startswith('_'):
                new_chem = old_chem
            else:
                new_chem = '_' + old_chem

            if old_chem != new_chem:
                curr_keys = self.keys()
                if new_chem in curr_keys:
                    self[new_chem] += self[old_chem]
                else:
                    self[new_chem] = self[old_chem]

                del self[old_chem]

def main():
    h_dryair = {
        'N2' : 78.084,
        'O2' : 20.947,
        'Ar' :  0.934,
        'CO2' : 0.0350
    }

    print("Test 1: Not static")
    cd_dryair = chemdist(h_dryair)

    print("  Unnormalized:")
    print(cd_dryair)

    print("\n  Normalizing:")
    cd_dryair.normalize_names()

    print("\n  Normalized:")
    print(cd_dryair)

    print("\n\nTest 2: Static")
    cd_dryair = chemdist(h_dryair)

    print("  Unnormalized:")
    print(cd_dryair)

    print("\n  Normalizing:")
    cd_dryair.normalize_names(force_static=True)

    print("\n  Normalized:")
    print(cd_dryair)

main()

以下是结果:

Test 1: Not static
  Unnormalized:
{'N2': 78.084, 'O2': 20.947, 'Ar': 0.934, 'CO2': 0.035}

  Normalizing:
raw_chems is dict_keys(['N2', 'O2', 'Ar', 'CO2'])
Found Argon!
Checking N2
Checking O2
Checking CO2
Checking _N2
Checking _O2
Checking _CO2

  Normalized:
{'Ar': 0.934, '_N2': 78.084, '_O2': 20.947, '_CO2': 0.035}


Test 2: Not static
  Unnormalized:
{'N2': 78.084, 'O2': 20.947, 'Ar': 0.934, 'CO2': 0.035}

  Normalizing:
raw_chems is ('N2', 'O2', 'Ar', 'CO2')
Found Argon!
Checking N2
Checking O2
Checking Ar
Checking CO2

  Normalized:
{'_N2': 78.084, '_O2': 20.947, '_Ar': 0.934, '_CO2': 0.035}

在测试1中,通过在'Ar'中明确搜索元素来找到元素raw_chems。但是,当迭代raw_chems时,元素'Ar'永远不会出现("Checking Ar"永远不会出现)。

在测试2中,raw_chems会转换为元组,"Checking Ar"会按预期显示。

此外,比较最终的规范化对象内容:在测试1中,'Ar'密钥未被'_Ar'替换,因为它在测试2中。'Ar'是唯一的密钥。测试1没有以下划线为前缀。

我对这个问题的原因有两个猜测:dict_keys有一些显而易见的每个人但是我的行为或一些奇怪的事情正在发生,因为我正在进行子类化dict。可能是两者的结合。

在有人试图改变我要问的问题之前,我试图解决的Big Picture问题是将dict键重命名为规范形式(HH,{{ 1}}和H2都表示相同的事情)。虽然对于某些人来说这可能是一个更有趣的问题,但我要问的是消失的密钥,所以请关注 x 而不是 y 你想象......:)

2 个答案:

答案 0 :(得分:0)

在python 3中dict.keys()是一个关于实际键和更改的视图,如果字典键发生了变化。如果迭代键,则不得更改字典的键。

答案 1 :(得分:0)

Python 3下的

dict.keys()将视图返回到字典的键中。当您更改字典的键时,您也会更改dict.keys()的内容。这可能会导致项目被跳过,就像在迭代列表时删除项目一样:

假设您即将处理O2

    N2
--> O2
    Ar
    CO2

您处理它并删除O2并添加_O2。在此过程中,Ar会滑入包含O2

的广告位
    N2
--> Ar
    CO2
    _O2

现在进入循环的下一次迭代,这将推进"当前项目"指向下一项的指针:

    N2
    Ar
--> CO2
    _O2

您刚刚跳过Ar

您可以找到一种解决方案:在迭代密钥之前复制密钥。另一种解决方案是构建新的字典而不是修改现有的字典。我通常推荐这种方法,因为它可以使你的代码在这个过程中更加清洁。

第三种解决方案是以相反的顺序迭代列表,这样当您删除列表时,您仍然需要处理的项目不会移动;但是,字典键不能保证按任何特定顺序排列。虽然它们属于您正在使用的Python版本,但您不应该依赖它。