как отсортировать список объектов на основе атрибута объектов?

<p>у меня есть список объектов python, которые я хочу отсортировать по атрибутам самих объектов. список выглядит так:</p><pre><code>>>> ut [<tag: 128>, <tag: 2008>, <tag: <>, <tag: actionscript>, <tag: addresses>, <tag: aes>, <tag: ajax> ...] </code></pre><p>каждый объект имеет количество:</p><pre><code>>>> ut[1].count 1l </code></pre><p>мне нужно отсортировать список по убыванию количества отсчетов.</p><p>я видел несколько методов для этого, но я ищу лучшую практику в python.</p>

<pre><code># 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) </code></pre><p>подробнее о <a href="http://wiki.python.org/moin/howto/sorting#sortingbykeys">сортировка по ключам "</a> </p>

<p>самый быстрый способ, особенно если в вашем списке много записей, это использовать <code>operator.attrgetter("count")</code>. однако это может выполняться на предоператорной версии python, поэтому было бы неплохо иметь резервный механизм. тогда вы можете сделать следующее:</p><pre><code>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 </code></pre>

<p>читатели должны заметить, что ключ = метод:</p><pre><code>ut.sort(key=lambda x: x.count, reverse=true) </code></pre><p>во много раз быстрее, чем добавление богатых операторов сравнения к объектам. я был удивлен, прочитав это (страница 485 "питона в двух словах"). вы можете подтвердить это, запустив тесты этой маленькой программы:</p><pre><code>#!/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 </code></pre><p>мои, очень минимальные, тесты показывают, что первый сорт более чем в 10 раз медленнее, но в книге говорится, что в целом он примерно в 5 раз медленнее. причина, по которой они говорят, из-за высокооптимизируемого алгоритма сортировки, используемого в python (<strong>timsort</strong>).</p><p>тем не менее, очень странно, что .sort (лямбда) быстрее, чем обычный старый .sort (). я надеюсь, что они исправят это.</p>

<pre><code>from operator import attrgetter ut.sort(key = attrgetter('count'), reverse = true) </code></pre>

<p><strong>объектно-ориентированный подход</strong></p><p>хорошей практикой является сделать логику сортировки объектов, если это применимо, свойством класса, а не включать его в каждом случае, когда требуется упорядочение.</p><p>это обеспечивает согласованность и устраняет необходимость в шаблонном коде.</p><p>как минимум, вы должны указать операции <code>\_\_eq\_\_</code> и <code>\_\_lt\_\_</code>, чтобы это работало. тогда просто используйте <code>sorted(list\_of\_objects)</code>.</p><pre><code>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] </code></pre>

<p>это очень похоже на список django экземпляров модели orm.</p><p>почему бы не отсортировать их по запросу так:</p><pre><code>ut = tag.objects.order\_by('-count') </code></pre>

<p>добавьте операторы расширенного сравнения в класс объекта, затем используйте метод sort () из списка.<br> смотрите <a href="http://docs.python.org/reference/datamodel.html#object.\_\_lt\_\_">богатое сравнение в python</a> .</p><hr><p><strong> обновление </strong>: хотя этот метод будет работать, я думаю, что решение от triptych лучше подходит для вашего случая, потому что это намного проще.</p>