【问题标题】:Avoiding KeyError when iterating over a list of dictionaries遍历字典列表时避免 KeyError
【发布时间】:2017-10-19 14:07:56
【问题描述】:

我有一个字典列表:

test = [{'first': '1'}, {'second': '2'}, {'first': '0'}, {'third': '3'}, {'fourth': '4'}]

但是当我这样做时:

stuff = [L['first'] for L in test]
print(stuff)

我明白了:

Traceback (most recent call last):
  File "C:/Users/User/Desktop/test_run.py", line 4, in <module>
    stuff = [L['first'] for L in test]
  File "C:/Users/User/Desktop/test_run.py", line 4, in <listcomp>
    stuff = [L['first'] for L in test]
KeyError: 'first'

我知道我可能犯了一个愚蠢的错误,但有什么帮助吗?

【问题讨论】:

  • 在您的列表中,并非所有字典都以 first 为键
  • 但在我只想要“第一”的东西中,我需要为此设置一个 if 条件吗?
  • 没有TypeError
  • 你期望结果是什么?
  • @Paul,是的。这样做 - stuff = [L['first'] for L in test if L.get('first')]

标签: python dictionary list-comprehension


【解决方案1】:

列表理解 + if

如果你想要所有的值,你需要先检查dict是否有对应的键:

>>> [d['first'] for d in test if 'first' in d]
['1', '0']
>>> [d['sixth'] for d in test if 'sixth' in d]
[]

只有一个值

如果您确定它们至少是一个具有'first' 值的字典,您可以使用next 来获取与第一次出现的'first' 对应的值:

>>> test = [{'first': '1'}, {'second': '2'}, {'first': '0'}, {'third': '3'}, {'fourth': '4'}]
>>> next(d['first'] for d in test if 'first' in d)
'1'

否则它会引发StopIteration

>>> next(d['sixth'] for d in test if 'sixth' in d)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

替代数据格式

最后,如果你经常做这个操作,稍微改变一下格式可能会很有趣:

from collections import defaultdict
data = defaultdict(list)

test = [{'first': '1'}, {'second': '2'}, {'first': '0'}, {'third': '3'}, {'fourth': '4'}]

for d in test:
    for k in d:
        data[k].append(d[k])

print(data)
# defaultdict(<type 'list'>, {'second': ['2'], 'fourth': ['4'], 'third': ['3'], 'first': ['1', '0']})
print(data['first'])
# ['1', '0']
print(data['sixth'])
# []

for 循环只需要一次,之后查找速度非常快。

【讨论】:

    【解决方案2】:

    这可以通过理解来解决,但我个人会简单地使用一个普通的 for 循环 - 主要是因为这样它不需要 需要 if 条件。包装为函数也可以很容易地重复使用(即用于其他键):

    def get_key(list_of_dicts, key):
        for dct in test:
            try:
                yield dct[key]
            except KeyError:
                pass
    

    这是一个生成器,因此您可以将其转换为列表,或在任何需要对其进行迭代的地方使用它:

    >>> list(get_key(test, 'first'))
    ['1', '0']
    

    【讨论】:

    • 为什么要依赖try/except 而不是简单的if 语句? PS:我没有投反对票。
    • 它也很酷,但是,您不认为在数据库管理方面这种方法可能会成为敌人吗?并且列表理解比这更有利。
    • @PaulNicolashunter 我们在谈论什么样的数据库管理?查询中的IF 或类似内容不会比列表理解或函数(在查询之后)更好吗?
    • 就像你必须在像 Mysql 这样的 DMS 中输入大量数据,我并不讨厌这种方法,但我的问题是哪种方法更方便。
    • @PaulNicolashunter 这是一个偏好问题。我经常使用列表推导,但我也经常使用这样的函数。如果你经常重用它,一个好名字的函数有时比一个硬编码的理解更好。但在这种情况下,理解可能会更好。 :)
    【解决方案3】:

    'test' 是带有可变键的字典列表。假设您只希望字典中的值以列表形式返回,键为“first”。我们可以的,

    test = [{'first': '1'}, {'second': '2'}, {'first': '0'}, {'third': '3'}, {'fourth': '4'}]
    stuff = [y['first'] for y in filter(lambda x: 'first' in x, test)]
    

    1) 过滤函数将只返回键为 'first' 的列表字典
    2) 使用该列表我们可以应用列表推导来获取值列表来自字典(使用键 'first')。

    希望这会有所帮助。

    【讨论】:

      【解决方案4】:

      L 正在迭代测试的元素。当 L 到达 {'second': '2'} L['first'] 时会抛出一个 key 错误。

      解决方案是

      [L.get('first') for L in test]

      当键不存在时,通过 get 方法访问字典元素不会抛出异常。相反,它将返回一个默认值。

      如果找不到键,您还可以传递自定义默认值,如下所示:

      [L.get('first','not_found') for L in test]

      如果您希望完全避免返回默认值,则必须在列表推导中引入条件:

      [L['first'] for L in test if 'first' in test]

      【讨论】:

        猜你喜欢
        • 2017-05-30
        • 2018-02-11
        • 2014-09-08
        • 2019-11-29
        相关资源
        最近更新 更多