【问题标题】:Counting entries in a list of dictionaries: for loop vs. list comprehension with map(itemgetter)计算字典列表中的条目:for 循环与使用 map(itemgetter) 的列表理解
【发布时间】:2011-02-28 12:55:46
【问题描述】:

在我正在编写的 Python 程序中,我比较了使用 for 循环和递增变量与使用 map(itemgetter)len() 计算列表中的字典条目时的列表理解。使用 each 方法需要相同的时间。我做错了什么还是有更好的方法?

这是一个大大简化和缩短的数据结构:

list = [
  {'key1': True, 'dontcare': False, 'ignoreme': False, 'key2': True, 'filenotfound': 'biscuits and gravy'},
  {'key1': False, 'dontcare': False, 'ignoreme': False, 'key2': True, 'filenotfound': 'peaches and cream'},
  {'key1': True, 'dontcare': False, 'ignoreme': False, 'key2': False, 'filenotfound': 'Abbott and Costello'},
  {'key1': False, 'dontcare': False, 'ignoreme': True, 'key2': False, 'filenotfound': 'over and under'},
  {'key1': True, 'dontcare': True, 'ignoreme': False, 'key2': True, 'filenotfound': 'Scotch and... well... neat, thanks'}
]

这是for循环版本:

#!/usr/bin/env python
# Python 2.6
# count the entries where key1 is True
# keep a separate count for the subset that also have key2 True

key1 = key2 = 0
for dictionary in list:
    if dictionary["key1"]:
        key1 += 1
        if dictionary["key2"]:
            key2 += 1
print "Counts: key1: " + str(key1) + ", subset key2: " + str(key2)

以上数据的输出:

Counts: key1: 3, subset key2: 2

这是另一个可能更 Pythonic 的版本:

#!/usr/bin/env python
# Python 2.6
# count the entries where key1 is True
# keep a separate count for the subset that also have key2 True
from operator import itemgetter
KEY1 = 0
KEY2 = 1
getentries = itemgetter("key1", "key2")
entries = map(getentries, list)
key1 = len([x for x in entries if x[KEY1]])
key2 = len([x for x in entries if x[KEY1] and x[KEY2]])
print "Counts: key1: " + str(key1) + ", subset key2: " + str(key2)

以上数据的输出(与之前相同):

Counts: key1: 3, subset key2: 2

我有点惊讶这些需要相同的时间。我想知道是否有更快的东西。我确定我忽略了一些简单的事情。

我考虑过的一种替代方法是将数据加载到数据库中并执行 SQL 查询,但数据不需要持久化,我必须分析数据传输等的开销以及数据库可能并不总是可用。

我无法控制数据的原始形式。

上面的代码不适用于样式点。

【问题讨论】:

    标签: python dictionary map loops list-comprehension


    【解决方案1】:

    我认为您通过大量开销(在顶级模块级别而不是在函数中运行,执行输出)淹没要测量的代码来测量不正确。将两个 sn-ps 放入名为 forloopwithmap 的函数中,并在列表的定义中添加 * 100(在关闭 ] 之后)以使测量结果在我的慢速笔记本电脑上变得有点实质性:

    $ py26 -mtimeit -s'import co' 'co.forloop()'
    10000 loops, best of 3: 202 usec per loop
    $ py26 -mtimeit -s'import co' 'co.withmap()'
    10 loops, best of 3: 601 usec per loop
    

    也就是说,使用 map 的所谓“更 Pythonic”的方法比普通的 for 方法慢三倍——这告诉你它并不是真的“更 Pythonic”;-)。

    优秀 Python 的标志是 简单,对我来说,它推荐了我傲慢地命名的东西......:

    def thebest():
      entries = [d['key2'] for d in list if d['key1']]
      return len(entries), sum(entries)
    

    在测量时,与forloop 方法相比,它节省了 10% 到 20% 的时间。

    【讨论】:

    • 至于覆盖测量的开销,正如我在我的问题中所说:“大大简化和缩短了数据结构”。
    • @Dennis,将代码放在模块顶层并不能简化事情——它只会给代码增加开销,从而扭曲速度。始终在函数中保留有意义的代码——无论是用于实际工作还是用于测量,这始终是最好的。
    • 这是我所说的“简化和缩短”的数据。
    • @Dennis,对,但这并没有改变“开销淹没测量”问题:顶级模块级代码每次设置或得到一个变量,因此与例如成正比通过循环的腿数。另外,正如我在 A 中提到的,我确实在数据上使用了* 100,以获得相当大的数据块进行测量,因此“缩短”有望被取消(我不能说“简化” -- 是否会影响相对性能取决于简化的内容;-)。
    • @Alex:哇! for i in range(10000000): a = i 在顶层比在函数中花费的时间长 50%。谢谢!通常我无论如何都会使用函数,但我只是在我的问题中发布了我认为是直截了当的测试代码(并将其手动挥舞为“不适用于风格点”)。正如他们所说,“你每天都会学到新东西”。
    猜你喜欢
    • 1970-01-01
    • 2017-04-05
    • 2018-08-28
    • 2018-11-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-28
    • 2023-03-11
    • 1970-01-01
    相关资源
    最近更新 更多