使用operator.itemgetter排序字典
刚刚在这里有个问题被提出来,关于如何根据字典的值来排序字典的键。
我几天前刚读到 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 个回答
itemgetter不支持嵌套(不过attrgetter是可以的)
你需要像这样把字典扁平化
sorted(([k]+v for k,v in mydict.iteritems()), key=itemgetter(2))
答案是——你不能这样做。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)))
注意,我并不推荐这样做 :)
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): ...
是一个匿名函数,它接受一个参数——一个包含两个元素的元组——并将其拆分为 k
和 v
。所以当这个 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