关于python:使用operator.itemgetter对字典进行排序

Sorting dictionary using operator.itemgetter

几分钟前,有人问了一个关于根据字典键的值排序的问题。

我刚读到几天前的operator.itemgetter排序方法,并决定尝试一下,但似乎不起作用。

并不是说我对这些问题的答案有任何问题,我只是想和operator.itemgetter一起尝试一下。

所以口述是:

1
2
3
4
5
6
>>> mydict = { 'a1': ['g',6],
           'a2': ['e',2],
           'a3': ['h',3],
           'a4': ['s',2],
           'a5': ['j',9],
           'a6': ['y',7] }

我试过这个:

1
2
3
>>> l = sorted(mydict.itervalues(), key=operator.itemgetter(1))
>>> l
[['e', 2], ['s', 2], ['h', 3], ['g', 6], ['y', 7], ['j', 9]]

这是我想要的。但是,由于我没有完整的字典(mydict.itervalues()),我尝试了以下方法:

1
>>> complete = sorted(mydict.iteritems(), key=operator.itemgetter(2))

这不管用(如我所料)。

那么,如何使用operator.itemgetter对dict进行排序,并在嵌套的键值对上调用itemgetter


1
2
3
4
5
6
7
8
In [6]: sorted(mydict.iteritems(), key=lambda (k,v): operator.itemgetter(1)(v))
Out[6]:
[('a2', ['e', 2]),
 ('a4', ['s', 2]),
 ('a3', ['h', 3]),
 ('a1', ['g', 6]),
 ('a6', ['y', 7]),
 ('a5', ['j', 9])]

关键参数始终是一个函数,一次从iterable(mydict.iteritems()中馈送一个项。在这种情况下,项目可能是

1
('a2',['e',2])

所以我们需要一个函数,可以把('a2',['e',2])作为输入,返回2。

lambda (k,v): ...是一个匿名函数,它接受一个参数(2元组)并将其解包到kv中。因此,当lambda函数应用于我们的项目时,k将是'a2'v将是['e',2]

lambda (k,v): operator.itemgetter(1)(v)适用于我们的产品,因此返回operator.itemgetter(1)(['e',2]),其中"itemgets"是['e',2]中的第二项,即2。

注意,lambda (k,v): operator.itemgetter(1)(v)不是用Python编写代码的好方法。正如gnibler指出的那样,每个项目都重新计算operator.itemgetter(1)。那是低效的。使用operator.itemgetter(1)的目的是创建一个可以多次应用的函数。您不希望每次都重新创建函数。lambda (k,v): v[1]可读性更强,速度更快:

1
2
3
4
5
In [15]: %timeit sorted(mydict.iteritems(), key=lambda (k,v): v[1])
100000 loops, best of 3: 7.55 us per loop

In [16]: %timeit sorted(mydict.iteritems(), key=lambda (k,v): operator.itemgetter(1)(v))
100000 loops, best of 3: 11.2 us per loop


itemgetter不支持嵌套(尽管attrgetter支持嵌套)

你得像这样把听筒压扁

1
sorted(([k]+v for k,v in mydict.iteritems()), key=itemgetter(2))


答案是——你不能。operator.itemgetter(i)返回一个可调用的,返回它的参数的i项,也就是说

1
2
f = operator.itemgetter(i)
f(d) == d[i]

它永远不会像《以东记》1(2)一样回归。如果您真的想以一种纯粹的功能风格来完成这项工作,您可以编写自己的compose()函数:

1
2
def compose(f, g):
    return lambda *args: f(g(*args))

和使用

1
2
sorted(mydict.iteritems(), key=compose(operator.itemgetter(1),
                                       operator.itemgetter(1)))

请注意,我不建议这样做:)


通常,索引la kv[1][1]更快:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
>>> from timeit import timeit
>>> setup = 'import operator; g = operator.itemgetter(1); '
>>> setup += 'd = {i: list(range(i+2)) for i in range(100)}'
>>> kwargs = {'setup': setup, 'number': 10000}

>>> timeit('sorted(d.items(), key=lambda kv: kv[1][1])', **kwargs)
0.5251589557155967

>>> timeit('sorted(d.items(), key=lambda kv: g(kv[1]))', **kwargs)
0.7175205536186695

>>> timeit('sorted(d.items(), key=lambda kv: g(kv)[1])', **kwargs)
0.7915238151326776

>>> timeit('sorted(d.items(), key=lambda kv: g(g(kv)))', **kwargs)
0.9781978335231543