【问题标题】:List Comprehension to Flatten a Dictionary of Dictionaries列表理解以展平词典词典
【发布时间】:2015-01-18 22:18:17
【问题描述】:

我有一本字典:

my_dict = {
    'a': {(1,2): True,
          (1,3): False},
    'b': {(1,4): True,
          (2,3): False}
}

字典总是这种形式,但每个“子”字典都有一组不同的键:my_dict['a'][(1,2)] 存在,但这并不意味着 my_dict['b'][(1,2)] 也存在。

我想要一个布尔值列表(无特定顺序):

[True, False, True, False]

我正在尝试使用单个列表推导来完成此操作:

[my_dict[letter][pair] for pair in my_dict[letter] for letter in my_dict]

这会引发错误:

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-142-dc1565efcdc8> in <module>()
      6 }
      7 
----> 8 [my_dict[letter][pair] for pair in my_dict[letter] for letter in my_dict]
KeyError: (2, 3)

它似乎在 my_dict['a'] 和 my_dict['b'] 中寻找 (2,3)。我认为我写的理解只会在适当的字典中查找键。

我见过this solution 可以使任何嵌套字典变平。我也知道我可以用命令式循环来暴力破解它。我只是想了解为什么列表理解不能按照我写的方式工作。

【问题讨论】:

    标签: python python-2.7 dictionary list-comprehension


    【解决方案1】:

    你想遍历值的值:

    [v for nested in outer.itervalues() for v in nested.itervalues()]
    

    请注意,循环需要按照嵌套方式进行排序;外循环优先:

    for nested in outer.itervalues():
        for v in nested.itervalues():
            # use v
    

    你把订单搞混了;你的代码只给出了KeyError,因为你有一个预先存在的letter global。

    演示:

    >>> my_dict = {
    ...     'a': {(1,2): True,
    ...           (1,3): False},
    ...     'b': {(1,4): True,
    ...           (2,3): False}
    ... }
    >>> [v for nested in my_dict.itervalues() for v in nested.itervalues()]
    [True, False, False, True]
    

    【讨论】:

    • 可能要注意,OP 可能不会像他们期望的那样得到[True, False, True, False] - 哦 - 没关系 - 没有看到 没有特定顺序 - 在这种情况还不如只使用Counter 而不是列表:)
    • 感谢您的解释,我很感激。当你进行列表推导时,我很难理解嵌套的顺序,尽管当你认为它是否像任何其他嵌套的 for 循环时是有道理的。出于某种原因,我认为换一种方式更有意义。
    • 另外,这似乎工作得很好:[my_dict[letter][pair] for letter in my_dict for pair in my_dict[letter]](只是切换顺序)
    • @elsherbini 也可以,只是效率不高。 :-) 但是,正确的顺序是关键。
    【解决方案2】:

    正如 elsherbini 所说,

    [my_dict[letter][pair] for letter in my_dict for pair in my_dict[letter]]
    

    这也有效:

    [little_dict[k] for little_dict in [my_dict[letter] for letter in my_dict] for k in little_dict]
    

    两者都产生[True, False, False, True]

    您想了解为什么您最初的尝试不起作用。

    [my_dict[letter][pair] for pair in my_dict[letter] for letter in my_dict]
    

    这个运行的唯一原因是你之前一定已经定义了letter,也许是之前运行一些类似的理解留下的定义。它首先尝试解释for pair in my_dict[letter],除非已经定义了letter,否则无法理解这一点。如果之前将字母定义为b(运行之前的列表解析后留下的值),那么它将pair 设置为my_dict['b'] 的键。然后它查看for letter in my_dict 并将字母设置为'a''b'。然后它会尝试评估第一部分 my_dict[letter][pair],但它使用来自 b 的键,因此当字母取值 'a' 时,这将不起作用。

    下面,我运行你的理解并得到NameError,然后运行另一个理解,它作为副作用设置letter 的值,然后我再次运行你的同样的理解并得到KeyError

    Python 2.6.9 
    >>> my_dict = {
    ...     'a': {(1,2): True,
    ...           (1,3): False},
    ...     'b': {(1,4): True,
    ...           (2,3): False}
    ... }
    >>> 
    >>> [my_dict[letter][pair] for pair in my_dict[letter] for letter in my_dict]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'letter' is not defined
    >>> letter
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'letter' is not defined
    >>> [letter for letter in my_dict]
    ['a', 'b']
    >>> letter
    'b'
    >>> [my_dict[letter][pair] for pair in my_dict[letter] for letter in my_dict]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    KeyError: (2, 3)
    >>> 
    

    请注意,KeyError 仅在(意外)设置了 letter 的值之后发生。第一次运行会生成 NameError

    【讨论】:

    • 啊,非常感谢您澄清了我收到 KeyError 的原因。这让我很难调试。
    猜你喜欢
    • 2022-12-31
    • 1970-01-01
    • 1970-01-01
    • 2019-08-15
    • 1970-01-01
    • 2023-02-02
    • 2017-03-29
    • 1970-01-01
    • 2014-05-25
    相关资源
    最近更新 更多