使用operator.itemgetter排序字典

20 投票
5 回答
43451 浏览
提问于 2025-04-16 09:57

刚刚在这里有个问题被提出来,关于如何根据字典的值来排序字典的键。

我几天前刚读到 operator.itemgetter 这个方法可以用来排序,所以我决定试试,但似乎没有成功。

我并不是对问题的答案有什么意见,只是想用 operator.itemgetter 来试试。

所以字典是这样的:

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

我尝试了这个:

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

这个方法达到了我想要的效果。不过,由于我没有完整的字典(mydict.itervalues()),我又试了这个:

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

但这个方法没有成功(正如我预期的那样)。

那么我该如何使用 operator.itemgetter 来排序字典,并在嵌套的键值对上调用 itemgetter 呢?

5 个回答

5

itemgetter不支持嵌套(不过attrgetter是可以的)

你需要像这样把字典扁平化

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

答案是——你不能这样做。operator.itemgetter(i) 返回一个可以调用的东西,它会返回它的参数中的第 i 个项目,也就是说

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

它永远不会返回像 d[i][j] 这样的东西。如果你真的想用纯函数的方式来做到这一点,你可以自己写一个 compose() 函数:

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

然后使用

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

注意,我并不推荐这样做 :)

33
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])]

这里的关键参数总是一个函数,它一次处理可迭代对象中的一个项目(mydict.iteritems())。在这个例子中,一个项目可能是这样的:

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

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

lambda (k,v): ... 是一个匿名函数,它接受一个参数——一个包含两个元素的元组——并将其拆分为 kv。所以当这个 lambda 函数应用到我们的项目时,k 就是 'a2',而 v 就是 ['e',2]

当我们把 lambda (k,v): operator.itemgetter(1)(v) 应用到我们的项目时,它返回的是 operator.itemgetter(1)(['e',2]),这个操作会“获取” ['e',2] 中的第二个元素,也就是 2。

需要注意的是,lambda (k,v): operator.itemgetter(1)(v) 这种写法在 Python 中并不是一个好的编码方式。正如 gnibbler 指出的那样,operator.itemgetter(1) 会对每一个项目重新计算,这样效率很低。使用 operator.itemgetter(1) 的目的是创建一个可以多次使用的函数。你不想每次都重新创建这个函数。相比之下,lambda (k,v): v[1] 更易读,而且速度更快:

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

撰写回答