为什么我在这个生成器中得到一个KeyError?

时间:2014-02-12 13:23:55

标签: python dictionary generator

我有以下词典:

d = {
    'A': {
        'param': {
            '1': {
                'req': True,
            },
            '2': {
                'req': True,
            },
        },
    },
    'B': {
        'param': {
            '3': {
                'req': True,
            },
            '4': {
                'req': False,
            },
        },
    },
}

我希望有一个生成器,它将为我提供每个第一级键,所需的参数。

req = {}
for key in d:
    req[key] = (p for p in d[key]['param'] if d[key]['param'][p].get('req', False))

因此,对于d中的每个密钥,仅当preq时才会获得参数True

但是,当我尝试使用我的生成器时,它会引发KeyError异常:

>>> req
{'A': <generator object <genexpr> at 0x27b8960>,
 'B': <generator object <genexpr> at 0x27b8910>}
>>> for elem in req['A']:
...     print elem
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-6-a96226f95cce> in <module>()
----> 1 for elem in req['A']:
      2     print elem
      3 

<ipython-input-4-1732088ccbdb> in <genexpr>((p,))
      1 for key in d:
----> 2         req[key] = (p for p in d[key]['param'] if d[key]['param'][p].get('req', False))
      3 

KeyError: '1'

2 个答案:

答案 0 :(得分:4)

您分配给req[key]的生成器表达式绑定在key变量上。但是key在循环中从'A'变为'B'。迭代第一个生成器表达式时,它会在key条件下将if评估为“B”,即使key在创建时为“A”。

绑定到变量值而不是它的引用的传统方法是将表达式用默认值包装在lambda中,然后立即调用它。

for key in d:
    req[key] = (lambda key=key: (p for p in d[key]['param'] if d[key]['param'][p].get('req', False)))()

结果:

1
2

答案 1 :(得分:3)

这是因为在执行生成器时,使用了key最新值。

假设for key in d:按顺序'A', 'B'迭代键,第一个生成器应该与key = 'A'一起使用,但由于关闭问题,它使用带{{的项目1}}作为关键。这没有'B'子条目。

更糟糕的是,生成器中的'1'变量有两个不同的值:key部分使用“正确”值,而for p in d[key]['param']使用“闭包值”,是最后一个。