统计字典列表中的条目:for循环与用map(itemgetter)的列表推导式

4 投票
1 回答
6726 浏览
提问于 2025-04-15 23:38

在我写的一个Python程序中,我比较了使用for循环和增加变量的方法,和用map(itemgetter)以及len()来计算列表中字典的条目数量。发现这两种方法所花的时间是一样的。我是不是做错了什么,或者有没有更好的方法呢?

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

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

这是另一种可能更符合Python风格的版本:

#!/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查询,但数据并不需要持久化,我还得考虑数据传输的开销等等,而且数据库并不总是可用。

我对数据的原始形式没有控制权。

上面的代码并不是为了追求风格。

1 个回答

12

我觉得你测量的方式不太对,因为你在测量代码的时候加了很多额外的负担(比如在顶层模块运行而不是放在函数里,或者进行输出)。把这两个代码片段放进名为 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

也就是说,所谓的“更符合 Python 风格”的方法 map 比普通的 for 方法慢了三倍——这说明它其实并不“更符合 Python 风格”;-)。

好的 Python 代码的标志是 简单,在我看来,这就是我自以为是地命名的...:

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

经过测量,这种方法比 forloop 方法节省了 10% 到 20% 的时间。

撰写回答