如何根据对象的属性对对象列表进行排序?

我有一个Python对象列表,我想按每个对象的特定属性排序:

>>> ut[Tag(name="toe", count=10), Tag(name="leg", count=2), ...]

如何按.count降序对列表进行排序?

830528 次浏览
# To sort the list in place...ut.sort(key=lambda x: x.count, reverse=True)
# To return a new list, use the sorted() built-in function...newlist = sorted(ut, key=lambda x: x.count, reverse=True)

更多关于按键排序

向对象类添加丰富的比较运算符,然后使用列表的sor()方法。
python中的丰富比较.


更新:虽然这种方法可行,但我认为Triptych的解决方案更适合您的情况,因为它更简单。

它看起来很像Django ORM模型实例的列表。

为什么不像这样对查询进行排序:

ut = Tag.objects.order_by('-count')

一种最快的方法,特别是如果您的列表有很多记录,是使用operator.attrgetter("count")。但是,这可能会在Python的前运算符版本上运行,因此最好有一个回退机制。那么,你可能想执行以下操作:

try: import operatorexcept ImportError: keyfun= lambda x: x.count # use a lambda if no operator moduleelse: keyfun= operator.attrgetter("count") # use operator since it's faster than lambda
ut.sort(key=keyfun, reverse=True) # sort in-place
from operator import attrgetterut.sort(key = attrgetter('count'), reverse = True)

读者应该注意到key=方法:

ut.sort(key=lambda x: x.count, reverse=True)

比向对象添加丰富的比较运算符快很多倍。读到这个我很惊讶(“Python in a Nutshell”第485页)。你可以通过在这个小程序上运行测试来确认这一点:

#!/usr/bin/env pythonimport random
class C:def __init__(self,count):self.count = count
def __cmp__(self,other):return cmp(self.count,other.count)
longList = [C(random.random()) for i in xrange(1000000)] #about 6.1 secslongList2 = longList[:]
longList.sort() #about 52 - 6.1 = 46 secslongList2.sort(key = lambda c: c.count) #about 9 - 6.1 = 3 secs

我的,非常小的,测试显示第一次排序慢了10倍以上,但书上说它一般只慢5倍左右。他们说的原因是由于python中使用的高度优化的排序算法(提姆排序)。

不过,很奇怪的是. sor(lambda)比普通的. sor()更快。我希望他们能解决这个问题。

面向对象方法

最好的做法是使对象排序逻辑(如果适用)成为类的属性,而不是在需要排序的每个实例中合并。

这确保了一致性并消除了对样板代码的需求。

至少,您应该指定__eq____lt__操作才能使其工作。然后只需使用sorted(list_of_objects)

class Card(object):
def __init__(self, rank, suit):self.rank = rankself.suit = suit
def __eq__(self, other):return self.rank == other.rank and self.suit == other.suit
def __lt__(self, other):return self.rank < other.rank
hand = [Card(10, 'H'), Card(2, 'h'), Card(12, 'h'), Card(13, 'h'), Card(14, 'h')]hand_order = [c.rank for c in hand]  # [10, 2, 12, 13, 14]
hand_sorted = sorted(hand)hand_sorted_order = [c.rank for c in hand_sorted]  # [2, 10, 12, 13, 14]

如果要排序的属性是财产,则可以避免导入operator.attrgetter,而是使用属性的#1方法。

例如,对于具有属性radius的类Circle,我们可以按半径对circles的列表进行排序,如下所示:

result = sorted(circles, key=Circle.radius.fget)

这不是最知名的功能,但通常会为我节省一条导入线。