且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

《Python Cookbook(第3版)中文版》——1.13 通过公共键对字典列表排序

更新时间:2022-09-27 23:01:09

本节书摘来自异步社区《Python Cookbook(第3版)中文版》一书中的第1章,第1.13节,作者[美]David Beazley , Brian K.Jones,陈舸 译,更多章节内容可以访问云栖社区“异步社区”公众号查看。

1.13 通过公共键对字典列表排序

1.13.1 问题

我们有一个字典列表,想根据一个或多个字典中的值来对列表排序。

1.13.2 解决方案

利用operator模块中的itemgetter函数对这类结构进行排序是非常简单的。假设通过查询数据库表项获取网站上的成员列表,我们得到了如下的数据结构:

rows = [
    {'fname': 'Brian', 'lname': 'Jones', 'uid': 1003},
    {'fname': 'David', 'lname': 'Beazley', 'uid': 1002},
    {'fname': 'John', 'lname': 'Cleese', 'uid': 1001},
    {'fname': 'Big', 'lname': 'Jones', 'uid': 1004}
]

根据所有的字典***有的字段来对这些记录排序是非常简单的,示例如下:

from operator import itemgetter

rows_by_fname = sorted(rows, key=itemgetter('fname'))
rows_by_uid = sorted(rows, key=itemgetter('uid'))

print(rows_by_fname)
print(rows_by_uid)

以上代码的输出为:

[{'fname': 'Big', 'uid': 1004, 'lname': 'Jones'},
 {'fname': 'Brian', 'uid': 1003, 'lname': 'Jones'},
 {'fname': 'David', 'uid': 1002, 'lname': 'Beazley'},
 {'fname': 'John', 'uid': 1001, 'lname': 'Cleese'}]

[{'fname': 'John', 'uid': 1001, 'lname': 'Cleese'},
 {'fname': 'David', 'uid': 1002, 'lname': 'Beazley'},
 {'fname': 'Brian', 'uid': 1003, 'lname': 'Jones'},
 {'fname': 'Big', 'uid': 1004, 'lname': 'Jones'}]

itemgetter()函数还可以接受多个键。例如下面这段代码:

rows_by_lfname = sorted(rows, key=itemgetter('lname','fname'))
print(rows_by_lfname)

这会产生如下的输出:

[{'fname': 'David', 'uid': 1002, 'lname': 'Beazley'},
 {'fname': 'John', 'uid': 1001, 'lname': 'Cleese'},
 {'fname': 'Big', 'uid': 1004, 'lname': 'Jones'},
 {'fname': 'Brian', 'uid': 1003, 'lname': 'Jones'}]

1.13.3 讨论

在这个例子中,rows被传递给内建的sorted()函数,该函数接受一个关键字参数key。这个参数应该代表一个可调用对象(callable),该对象从rows中接受一个单独的元素作为输入并返回一个用来做排序依据的值。itemgetter()函数创建的就是这样一个可调用对象。

函数operator.itemgetter()接受的参数可作为查询的标记,用来从rows的记录中提取出所需要的值。它可以是字典的键名称、用数字表示的列表元素或是任何可以传给对象的__getitem__()方法的值。如果传多个标记给itemgetter(),那么它产生的可调用对象将返回一个包含所有元素在内的元组,然后sorted()将根据对元组的排序结果来排列输出结果。如果想同时针对多个字段做排序(比如例子中的姓和名),那么这是非常有用的。

有时候会用lambda表达式来取代itemgetter()的功能。例如:

rows_by_fname = sorted(rows, key=lambda r: r['fname'])
rows_by_lfname = sorted(rows, key=lambda r: (r['lname'],r['fname']))

这种解决方案通常也能正常工作。但是用itemgetter()通常会运行得更快一些。因此如果需要考虑性能问题的话,应该使用itemgetter()。

最后不要忘了本节中所展示的技术同样适用于min()和max()这样的函数。例如:

>>> min(rows, key=itemgetter('uid'))
{'fname': 'John', 'lname': 'Cleese', 'uid': 1001}
>>> max(rows, key=itemgetter('uid'))
{'fname': 'Big', 'lname': 'Jones', 'uid': 1004}
>>>