【问题标题】:Extract values for multiple keys from list of dictionary从字典列表中提取多个键的值
【发布时间】:2021-09-17 10:59:49
【问题描述】:

我是 Python 新手,正在寻求帮助。我的字典列表如下:

data = [
    {'device_id': 'D11', 'ipv4': '1.1.1.1'},
    {'device_id': 'D42', 'ipv4': '1.1.1.2'},
    {'device_id': 'D32', 'hostname': 'MVPQ021'},
    {'device_id': 'D94', 'ipv6': '2001:0db8:0:7334'}
]

我只需要获取 IP 地址列表如下:

['1.1.1.1', '1.1.1.2', '2001:0db8:0:7334']

我尝试了以下方法,得到了我想要的结果:

result = []

    for x in data:
        if 'ipv4' in x.keys():
            result.append(x['ipv4'])
            continue
        if 'ipv6' in x.keys():
            result.append(x['ipv6'])

但是,对于非常长的列表,例如包含 1000 多个元素,此解决方案效率不高。

是否有任何优化的方法可以只过滤掉 ipv4ipv6 值而不迭代列表中的每个元素?

【问题讨论】:

  • 对于 1000+ ?多少钱 ?它应该一秒钟完成。我在 4 秒内跑了 10M
  • 我怀疑这个循环在其他循环中
  • 我们所说的 + 比 1000 多多少?除非在马铃薯电脑上执行,否则它应该非常很大,然后才会注意到性能问题。
  • @SakshmKhare,如果您使用的是 Python 3.8+。我添加了一个答案以利用海象运算符并使用列表理解(无循环)。
  • @lmiguelvargasf 要清楚,仍然存在一个 循环,您必须访问每个元素。同样从某种意义上说,列表推导被实现为 Python 级别的 for 循环,末尾带有 list.append。它们可能会稍微快一些,因为 list.append 已缓存(您不必解析属性 .append),而且我相信还有一个小的字节码技巧。

标签: python list dictionary optimization time-complexity


【解决方案1】:

假设您使用的是 Python 3.8+,您可以按如下方式利用 :=(海象运算符),并且可以在一个列表理解中完成:

data = [
    {'device_id': 'D11', 'ipv4': '1.1.1.1'},
    {'device_id': 'D42', 'ipv4': '1.1.1.2'},
    {'device_id': 'D32', 'hostname': 'MVPQ021'},
    {'device_id': 'D94', 'ipv6': '2001:0db8:0:7334'}
]
result = [
    ip_address for e in data
    if (ip_address := e.get('ipv4') or e.get('ipv6')) is not None
]

解释:

  1. 对于列表中的每个字典,您尝试获取键 'ipv4''ipv6' 的值并将其存储在 ip_address 中。
  2. 如果两者都没有值,它将跳过该元素。

【讨论】:

    【解决方案2】:

    x.keys() 更改为x,我的测试您将获得大约 20-30% 的收益。

    for x in data:
        if 'ipv4' in x:
            result.append(x['ipv4'])
            continue
        if 'ipv6' in x:
            result.append(x['ipv6'])
    

    对我有一些好处

    • 10M 在 3.2 秒内
    • 17 秒内 50M

    【讨论】:

    • 没试过,但我怀疑使用mapitemgetter 可能会多节省几毫秒。也许还可以稍微改变逻辑,这样就不需要每次迭代多次计算相同的哈希值会有所帮助,但除非我们计算实际时间,否则这属于过早优化的领域
    • 使用 try-except 可能会更快,具体取决于数据的性质
    • 此外,对于 OP 来说可能并不明显,但是将所有内容放在一个函数中以便所有查找都是本地的肯定会加快速度。再次,勉强。
    • 另外,我想知道,使用if ... elif 会更快吗?由于我们在那个领域,另一个明显的微优化是缓存属性解析result_append = result.append并在循环中使用result_append(x["whatever"])
    • @DeepSpace 我确实使用了mapitemgetter 作为list(map(itemgetter('ipv4'), data)) 但抛出了KeyError
    【解决方案3】:

    尝试更实用的风格一班:

    ip_list = list(filter(None, map(lambda i: i.get('ipv4', i.get('ipv6')), data)))
    

    【讨论】:

    • 您是否有一些数据支持声称它比 OP 的代码更快?
    • 您为什么认为这会更快?一般来说,“单线”并不意味着“更快”。但具体来说,在这种情况下,当您首先找到'ipv4' 时,您永远不会短路。其次,您使用的是list(<gen exp>),这将比列表推导慢,尽管列表推导可能比循环略快。此外,您现在有一个 filter 对象列表......这不是 OP 想要的。
    • 对我来说 ~73M 文件处理了 8.11 秒。它不是“过滤器列表”,列表只是将可迭代转换为实际列表对象。
    • 一般来说,“单行”并不意味着“更快”——你说得对,但通常更快的是“功能性”。大多数时候,函数式风格对于大型数据集来说更快。使用“if 语句”比较慢。
    • 你说得对,我看错了。然后,如果您正在执行过滤操作,则应该使用列表推导,通常filter 会比等效循环慢,即使对于大型数据集也是如此。函数式风格在 CPython 中一般不会更快
    猜你喜欢
    • 1970-01-01
    • 2021-09-26
    • 2019-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-28
    • 1970-01-01
    相关资源
    最近更新 更多