我有一个python对象列表,我想按对象本身的属性排序。列表如下:
>>> ut
[, , , , ,
, ...]
每个对象都有一个计数:
>>> ut[1].count
1L
我需要按计数降序对列表进行排序。
我已经看到了几种方法,但我正在寻找Python中的最佳实践。
dupe:stackoverflow.com/questions/157424/…,stackoverflow.com/questions/222752/…,stackoverflow.com/questions/327191/…
为那些在Python中寻找更多排序信息的人排序。
除了operator.attrgetter("attribute_name")之外,您还可以使用functors作为键,如object_list.sort(key=my_sorting_functor("my_key"),从而故意放弃实现。
# 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)
有关按键排序的更多信息»;
没问题。顺便说一句,如果Muhuk是对的,并且它是Django对象的列表,那么您应该考虑他的解决方案。然而,对于排序对象的一般情况,我的解决方案可能是最佳实践。
在大列表中,使用operator.attrgetter("count")作为键可以获得更好的性能。这只是这个答案中lambda函数的一种优化(低级)形式。
谢谢你的回答。如果它是一个字典列表,并且"count"是它的键之一,则需要如下更改:ut.sort(key=lambda x:x["count"],reverse=true)
最快的方法是使用operator.attrgetter("count"),尤其是如果您的列表中有很多记录。但是,这可能在操作前版本的Python上运行,所以最好有一个回退机制。您可能需要执行以下操作:
try: import operator
except ImportError: keyfun= lambda x: x.count # use a lambda if no operator module
else: keyfun= operator.attrgetter("count") # use operator since it's faster than lambda
ut.sort(key=keyfun, reverse=True) # sort in-place
在这里,我将使用变量名"keyun"而不是"cmpun",以避免混淆。sort()方法也通过cmp=参数接受比较函数。
如果对象具有动态添加的属性(如果在__init__方法之后执行了self.__dict__ = {'some':'dict'},则这似乎不起作用。但我不知道为什么会不同。
@图卡:我从来没有替换过实例__dict__。注意,"动态添加属性的对象"和"设置对象的__dict__属性"几乎是正交概念。我是说,因为您的评论似乎意味着设置__dict__属性是动态添加属性的要求。
@Tzot:我现在看到的是:github.com/randomatic technologies/goatfish/blob/master/…,这里使用该迭代器:github.com/tallertechnologies/dishey/blob/master/app.py l28会引发属性错误。也许是因为Python3,但是…
@图卡:我会用self.__dict__.update(kwargs)代替self.__dict__= kwargs。在任何情况下,也许这是一个python 3问题,因为2.7.3看起来运行正常。稍后我将使用python 3进行研究。
然后就是这样,这可能意味着类模型的元类在这里是错误的。
@Tzot,它与Django无关,goatfish元属性只是一个没有魔法的原始对象…我已经在一个Python2.7项目中测试过了它,并且看起来像预期的那样工作。我需要进一步了解这个问题…
@Tzot:如果我了解operator.attrgetter的用法,我可以提供一个具有任何属性名的函数,并返回一个已排序的集合。
对于那些想了解更多信息的人:wiki.python.org/moin/howto/sorting operator_module_函数
读者应该注意到key=方法:
ut.sort(key=lambda x: x.count, reverse=True)
比向对象添加丰富的比较运算符快很多倍。我很惊讶地读到了这篇文章(第485页的"简而言之,Python")。您可以通过在这个小程序上运行测试来确认这一点:
#!/usr/bin/env python
import 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 secs
longList2 = longList[:]
longList.sort() #about 52 - 6.1 = 46 secs
longList2.sort(key = lambda c: c.count) #about 9 - 6.1 = 3 secs
我的,非常小的,测试显示第一种速度慢了10倍以上,但书中说一般来说只有5倍左右。他们说这是因为python(timsort)中使用了高度优化的排序算法。
不过,非常奇怪的是.sort(lambda)比普通的old.sort()更快。我希望他们能解决这个问题。
from operator import attrgetter
ut.sort(key = attrgetter('count'), reverse = True)
面向对象方法
如果适用,最好将对象排序逻辑设置为类的属性,而不是将其合并到需要排序的每个实例中。
这确保了一致性,并消除了对样板代码的需求。
至少,您应该指定__eq__和__lt__操作,这样才能工作。那就用sorted(list_of_objects)。
class Card(object):
def __init__(self, rank, suit):
self.rank = rank
self.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]
考虑到最初的IMO用例,这是一个更聪明的解决方案——我认为这应该是投票率最高的答案。
它看起来很像django-orm模型实例的列表。
为什么不在查询时这样排序:
ut = Tag.objects.order_by('-count')
是的,但是使用django标记,所以我使用一个内置的工具来抓取特定查询集使用的标记集,如so:tag.objects.usage for_queryset(query set,counts=true)
向对象类添加丰富的比较运算符,然后使用列表的sort()方法。请参阅python中的丰富比较。
更新:虽然这个方法可行,但我认为TripTych的解决方案更适合您的情况,因为它更简单。