列表值的字典推导式

7
我想知道是否有更Pythonic的方法来完成以下操作,也许可以使用字典推导式:
A = some list
D = {}
for i,v in enumerate(A):
    if v in D:
        D[v].append(i)
    else:
        D[v] = [i]
2个回答

12

使用 defaultdict

from collections import defaultdict
D = defaultdict(list)
[D[v].append(i) for i, v in enumerate(A)]

使用setdefault

D = {}
[D.setdefault(v, []).append(i) for i, v in enumerate(A)]

我无法想到任何使用字典推导而不对数据进行排序的方法:

from itertools import groupby
from operator import itemgetter
{v: ids for v, ids in groupby(enumerate(sorted(A)), itemgetter(1))}

表演:

from collections import defaultdict
from itertools import groupby
from operator import itemgetter
from random import randint

A = tuple(randint(0, 100) for _ in range(1000))

def one():
    D = defaultdict(list)
    [D[v].append(i) for i, v in enumerate(A)]

def two():
    D = {}
    [D.setdefault(v, []).append(i) for i, v in enumerate(A)]

def three():
    {v: ids for v, ids in groupby(enumerate(sorted(A)), itemgetter(1))}


from timeit import timeit

for func in (one, two, three):
    print(func.__name__ + ':', timeit(func, number=1000))

结果(一如既往,最简单的获胜):

one: 0.25547646999984863
two: 0.3754340969971963
three: 0.5032370890003222

1
使用列表推导式仅作为完成工作的手段真的符合Pythonic吗?我承认,我以前遇到过这个问题,并考虑过使用list-comps来解决问题,但它感觉很hacky。不过,除此之外,我同意defaultdict是最清晰的答案。 - dwanderson
另一种方法是使用map,但列表推导式更易读,并且通常更受欢迎。参考链接:https://dev59.com/mHM_5IYBdhLWcg3wvV5w#1247490 和 http://www.u.arizona.edu/~erdmann/mse350/topics/list_comprehensions.html。 - aluriak

1
你可以做以下事情。
d = collections.defaultdict(list)
for i,v in enumerate(A):
    d[v].append(i)

你可以看到生成的字典的值是列表,其中的元素需要在遍历时产生。如果您坚持使用字典推导式,则必须先找到所有的 (value, [indices]),然后在 [(k,[v])] 上执行字典推导式,这只会增加额外的花招而没有任何好处。

实际上,从查看所选答案得出了这个解决方案。 - lorenzocastillo
@user2804747 很好。如果你正在处理主要使用字典进行数据转换,那么 collections 模块值得一读。 - C Panda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接