Как отсортировать список объектов по атрибуту объектов?
У меня есть список объектов на Python, который я хочу отсортировать по определенному атрибуту каждого объекта. Вот пример списка:
[Tag(name="toe", count=10), Tag(name="leg", count=2), ...]
Как мне отсортировать этот список по атрибуту .count
в порядке убывания?
5 ответ(ов)
Способ, который может быть наиболее быстрым, особенно если у вас большой список записей, — это использовать operator.attrgetter("count")
. Однако это может не работать в старых версиях Python, поэтому было бы неплохо иметь механизм резервирования. Вам может понадобиться сделать следующее:
try:
import operator
except ImportError:
keyfun = lambda x: x.count # используем lambda, если модуль operator недоступен
else:
keyfun = operator.attrgetter("count") # используем operator, так как он быстрее, чем lambda
ut.sort(key=keyfun, reverse=True) # сортируем на месте
Таким образом, вы создаете универсальное решение, которое будет работать даже если модуль operator
недоступен, обеспечивая тем самым совместимость с более старыми версиями Python.
Согласно вашему вопросу, ключевой метод key=
:
ut.sort(key=lambda x: x.count, reverse=True)
в несколько раз быстрее, чем добавление оператора сравнения к объектам. Я сам был удивлён, прочитав это на странице 485 книги "Python in a Nutshell". Вы можете подтвердить это, запустив тесты на следующей программе:
#!/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)] # около 6.1 секунды
longList2 = longList[:]
longList.sort() # около 52 - 6.1 = 46 секунд
longList2.sort(key=lambda c: c.count) # около 9 - 6.1 = 3 секунды
Мои, весьма минимальные, тесты показывают, что первый способ сортировки работает более чем в 10 раз медленнее, хотя в книге говорится, что в общем случае это только около 5 раз медленнее. Причина этого заключается в высокой оптимизации используемого алгоритма сортировки в Python (timsort).
Тем не менее, довольно странно, что .sort(lambda)
быстрее, чем простой .sort()
. Я надеюсь, что это когда-нибудь исправят.
Ваш код использует функцию sort
для сортировки списка объектов ut
по атрибуту count
в порядке убывания. Для этого вы импортируете attrgetter
из модуля operator
, который удобен для извлечения атрибутов из объектов.
Чтобы отвечать на ваш вопрос, вот переведённый фрагмент кода на русский:
from operator import attrgetter
ut.sort(key=attrgetter('count'), reverse=True)
В данном примере ut
— это список объектов, и метод sort
сортирует его по атрибуту count
в порядке убывания. Если вам нужно более детальное объяснение или примеры, пожалуйста, уточните ваш вопрос!
Объектно-ориентированный подход
Хорошей практикой является размещение логики сортировки объектов в классе, если это применимо, а не в каждом экземпляре, где требуется сортировка.
Это обеспечивает однородность и исключает необходимость в шаблонном коде.
Минимально вам следует реализовать операции __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]
Таким образом, вы можете организовать сортировку объектов без дублирования кода, и ваш класс будет сам управлять своей логикой сортировки.
Скорее всего, это выглядит как список экземпляров моделей Django ORM.
Почему бы не отсортировать их на этапе запроса следующим образом:
ut = Tag.objects.order_by('-count')
Как отсортировать список словарей по значению словаря в Python?
Как отсортировать список/кортеж списков/кортежей по элементу на заданном индексе
Как получить последний элемент списка?
Как клонировать список, чтобы он не изменялся неожиданно после присваивания?
Самый быстрый способ проверить наличие значения в списке