Python:如何按多个值对字典列表进行排序?

66

我想按照一个值和另一个值来对列表进行排序。有没有简单的方法可以做到这一点?以下是一个小例子:

A = [{'name':'john','age':45},
     {'name':'andi','age':23},
     {'name':'john','age':22},
     {'name':'paul','age':35},
     {'name':'john','age':21}]

这个命令是用于按'name'对此列表进行排序的:

sorted(A, key = lambda user: user['name'])

但是我该如何按第二个值对此列表进行排序呢?就像这个例子中的'age'一样。

我想要这样的排序(首先按'name'排序,然后按'age'排序):

andi - 23
john - 21
john - 22
john - 45
paul - 35

谢谢!


5
顺便提一下:Python的排序功能保证是稳定的,因此你可以通过先按“年龄”排序再按“姓名”排序,就能得到你想要的结果。(请注意键是以相反的顺序排列的。你需要先按第二个键排序,然后再按第一个键排序。) - Bakuriu
3个回答

93
>>> A = [{'name':'john','age':45},
     {'name':'andi','age':23},
     {'name':'john','age':22},
     {'name':'paul','age':35},
     {'name':'john','age':21}]
>>> sorted(A, key = lambda user: (user['name'], user['age']))
[{'age': 23, 'name': 'andi'}, {'age': 21, 'name': 'john'}, {'age': 22, 'name': 'john'}, {'age': 45, 'name': 'john'}, {'age': 35, 'name': 'paul'}]

这将按照两个属性的元组排序,以下是等效的且速度更快/更简洁的代码:

>>> from operator import itemgetter
>>> sorted(A, key=itemgetter('name', 'age'))
[{'age': 23, 'name': 'andi'}, {'age': 21, 'name': 'john'}, {'age': 22, 'name': 'john'}, {'age': 45, 'name': 'john'}, {'age': 35, 'name': 'paul'}]

评论中提到:@Bakuriu

我敢打赌两者之间没有太大的区别,但是itemgetter避免了一点开销,因为它在单个操作码(CALL_FUNCTION)期间提取键并创建了tuple,而调用lambda将不得不调用函数,加载各种常量(这些是其他字节码),最后调用下标(BINARY_SUBSCR),构建tuple并返回它... 这对解释器来说是更多的工作。

总之:itemgetter使执行完全保持在C级别上,因此尽可能快。


3
为什么使用itemgetter比lambda表达式更快呢?它们不是要进行相同的查找吗? - catchmeifyoutry
4
我打赌这两者之间没有太大的区别,但 itemgetter 可以避免一些额外的开销,因为它可以在单个操作码(CALL_FUNCTION)期间提取键并创建元组。而调用lambda函数将不得不调用函数,加载各种常量(这些是其他字节码),最后调用下标(BINARY_SUBSCR),构建元组并返回... 这对解释器来说需要更多的工作。 - Bakuriu
@Bakuriu 谢谢你的解释。因此,itemgetter实现在cpython中是优化的c代码,而不仅仅是在其在线文档中提到的参考python代码的实现。 - catchmeifyoutry
3
注意,文档说明它与Python代码是等价的,而不是以相同的方式实现。特别是,itemgetter实际上是一个类型(参见type(operator.itemgetter)),因此它甚至不是一个真正的函数。这很典型:许多内置函数都是用C实现的,通常它们是类型而不是函数(例如enumerate)。 - Bakuriu
1
嗨@jamylak,我之前不知道我们可以使用*来迭代列表元素并传递给itermgetter()。这太棒了,现在它正常工作了。非常感谢! - Xavier Sun
显示剩余2条评论

53
from operator import itemgetter

sorted(your_list, key=itemgetter('name', 'age'))

2
你是否想知道operator.itemgetter是否有些神奇?它其实并没有。它返回一个元组(在这种情况下是两个元素),然后sorted会对其做正确的事情 - Lutz Prechelt
好的回答。但必须像下面这样使用:your_list_sorted = sorted(your_list, key=itemgetter('name','age')) - Alexey Antonenko

0
这里有一种替代性的通用解决方案——按键和值对字典元素进行排序。它的优点是不需要指定键,并且如果某些字典中缺少一些键,它仍然可以正常工作。
def sort_key_func(item):
    """ helper function used to sort list of dicts

    :param item: dict
    :return: sorted list of tuples (k, v)
    """
    pairs = []
    for k, v in item.items():
        pairs.append((k, v))
    return sorted(pairs)

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