且构网

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

《Python Cookbook(第3版)中文版》——1.14 对不原生支持比较操作的对象排序

更新时间:2022-09-27 18:26:05

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

1.14 对不原生支持比较操作的对象排序

1.14.1 问题

我们想在同一个类的实例之间做排序,但是它们并不原生支持比较操作。

1.14.2 解决方案

内建的sorted()函数可接受一个用来传递可调用对象(callable)的参数key,而该可调用对象会返回待排序对象中的某些值,sorted则利用这些值来比较对象。例如,如果应用中有一系列的User对象实例,而我们想通过user_id属性来对它们排序,则可以提供一个可调用对象将User实例作为输入然后返回user_id。示例如下:

>>> class User:
...      def __init__(self, user_id):
...           self.user_id = user_id
...      def __repr__(self):
...           return 'User({})'.format(self.user_id)
...
>>> users = [User(23), User(3), User(99)]
>>> users
[User(23), User(3), User(99)]
>>> sorted(users, key=lambda u: u.user_id)
[User(3), User(23), User(99)]
>>>

除了可以用lambda表达式外,另一种方式是使用operator.attrgetter()。

>>> from operator import attrgetter
>>> sorted(users, key=attrgetter('user_id'))
[User(3), User(23), User(99)]
>>>

1.14.3 讨论

要使用lambda表达式还是attrgetter()或许只是一种个人喜好。但是通常来说,attrgetter()要更快一些,而且具有允许同时提取多个字段值的能力。这和针对字典的operator.itemgetter()的使用很类似(参见1.13节)。例如,如果User实例还有一个first_name和last_name属性的话,可以执行如下的排序操作:

by_name = sorted(users, key=attrgetter('last_name', 'first_name'))

同样值得一提的是,本节所用到的技术也适用于像min()和max()这样的函数。例如:

>>> min(users, key=attrgetter('user_id')
User(3)
>>> max(users, key=attrgetter('user_id')
User(99)
>>>