Python: сортировка списков методом .sort() с ключом — простыми словами
Поводом опубликовать пост стало то, что при детальном изучении списков (массивов) в Python я не смог найти в сети ни одного простого описания метода сортировки элементов с использованием ключа: list.sort(key=. ).
Может быть, конечно, это мне так не повезло и я долго понимаю простые для всех вещи, однако я думаю, что приведенная ниже информация будет весьма полезна таким же начинающим питонистам, как и я сам.
Итак, что мы имеем. Предположим, у нас есть список, который мы бы хотели отсортировать — и состоит он из трех строк разной длины в определенной последовательности:
sortList = [‘a’, ‘сс’, ‘bbb’]
Сортировка элементов массива методом .sort() производится по умолчанию лексикографически — проще говоря, в алфавитном порядке, а также от меньшего значения к большему. Поэтому если мы выполним:
то получим на выходе:
Однако метод .sort() позволяет нам изменять и принцип, и порядок сортировки.
Для изменения принципа сортировки используется ключевое слово key, которое стало доступным начиная с версии Python 2.4.
Предположим, нам хотелось бы отсортировать наш список двумя способами: 1. в алфавитном порядке; 2. по длине строки. Первый способ, впрочем, уже работает как сортировка по умолчанию, однако мы можем добиться таких же результатов и с помощью параметра key:
sortList = [‘a’, ‘cc’, ‘bbb’]
# Создаем «внешнюю» функцию, которая будет сортировать список в алфавитном порядке:
def sortByAlphabet(inputStr):
return inputStr[0] # Ключом является первый символ в каждой строке, сортируем по нему
# Вторая функция, сортирующая список по длине строки:
def sortByLength(inputStr):
return len(inputStr) # Ключом является длина каждой строки, сортируем по длине
print u’Исходный список: ‘, sortList # >>> [‘a’, ‘cc’, ‘bbb’]
sortList.sort(key=sortByAlphabet) # Каждый элемент массива передается в качестве параметра функции
print u’Отсортировано в алфавитном порядке: ‘, sortList # >>> [‘a’, ‘bbb’, ‘cc’]
sortList.sort(key=sortByLength) # Каждый элемент массива передается в качестве параметра функции
print u’Отсортировано по длине строки: ‘, sortList # >>> [‘a’, ‘cc’, ‘bbb’]
# Теперь отсортируем по длине строки, но в обратном порядке:
sortList.sort(key=sortByLength, reverse=True) # В обратном порядке
print u’Отсортировано по длине строки, в обратном порядке: ‘, sortList # >>> [‘bbb’, ‘cc’, ‘a’]
Обратите внимание, что метод .sort() производит действия с исходным списком, переставляя элементы внутри него самого, и НЕ возвращает отсортированную копию исходного списка. Для получения отсортированной копии нужно использовать метод sorted:
— либо такой же вариант, но с параметром key (аналогично описанному выше):
newList = sorted(sortList, key=sortByLength)
У метода .sorted() есть и другие параметры, но мне они показались не настолько запутанными для самостоятельного разбора.
Всё о сортировке в Python: исчерпывающий гайд
Сортировка в Python выполняется с помощью sorted() и list.sort(). Разбираем на примерах, как это работает.
Сортировка в Python выполняется функцией sorted() , если это итерируемые объекты, и методом list.sort() , если это список. Рассмотрим подробнее, как это работало в старых версиях и как работает сейчас.
Примечание Вы читаете улучшенную версию некогда выпущенной нами статьи.
Разработка на Python с нуля: роадмап программиста
Основы сортировки
Так как отсортировать список Python? Для сортировки по возрастанию достаточно вызвать функцию сортировки Python sorted() , которая вернёт новый отсортированный список:
>>> sorted([5, 2, 3, 1, 4]) [1, 2, 3, 4, 5]
Для сортировки списка Python также можно использовать метод списков list.sort() , который изменяет исходный список (и возвращает None во избежание путаницы). Обычно Python sort list не так удобен, как использование sorted() , но если вам не нужен исходный список, то так будет немного эффективнее:
>>> a = [5, 2, 3, 1, 4] >>> a.sort() >>> a [1, 2, 3, 4, 5]
Прим.перев. В Python вернуть None и не вернуть ничего — одно и то же.
Ещё одно отличие заключается в том, что метод list.sort() определён только для списков, в то время как функция sorted Python работает со всеми итерируемыми объектами. Грубо говоря, функция sort Python сортирует список и сохраняет его в отсортированном виде, в то время как функция sorted Питон создаёт новый отсортированный список без изменения исходного.
>>> sorted() [1, 2, 3, 4, 5]
Прим.перев. При итерировании по словарю Python возвращает его ключи. Если вам нужны их значения или пары «ключ-значение», используйте методы dict.values() и dict.items() соответственно.
Рассмотрим основные функции сортировки Python.
Функции-ключи
С версии Python 2.4 у list.sort() и sorted() появился параметр key для указания функции, которая будет вызываться на каждом элементе до сравнения. Вот регистронезависимое сравнение строк:
>>> sorted("This is a test string from Andrew".split(), key=str.lower) ['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']
Значение key должно быть функцией, принимающей один аргумент и возвращающей ключ для сортировки. Работает быстро, потому что функция-ключ вызывается один раз для каждого элемента.
Часто можно встретить код, где сложный объект сортируется по одному из его индексов. Например:
>>> student_tuples = [ ('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10), ] >>> sorted(student_tuples, key=lambda student: student[2]) # сортируем по возрасту [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
Тот же метод работает для объектов с именованными атрибутами:
>>> class Student: def __init__(self, name, grade, age): self.name = name self.grade = grade self.age = age def __repr__(self): return repr((self.name, self.grade, self.age)) def weighted_grade(self): return 'CBA'.index(self.grade) / self.age >>> student_objects = [ Student('john', 'A', 15), Student('jane', 'B', 12), Student('dave', 'B', 10), ] >>> sorted(student_objects, key=lambda student: student.age) # сортируем по возрасту [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
Функции модуля operator
Показанные выше примеры функций-ключей встречаются настолько часто, что Python предлагает удобные функции, чтобы сделать всё проще и быстрее. Модуль operator содержит функции itemgetter() , attrgetter() и, начиная с Python 2.6, methodcaller() . С ними всё ещё проще:
>>> from operator import itemgetter, attrgetter, methodcaller >>> sorted(student_tuples, key=itemgetter(2)) [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)] >>> sorted(student_objects, key=attrgetter('age')) [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
Функции operator дают возможность использовать множественные уровни сортировки массива Python. Отсортируем учеников сначала по оценке, а затем по возрасту:
>>> sorted(student_tuples, key=itemgetter(1, 2)) [('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)] >>> sorted(student_objects, key=attrgetter('grade', 'age')) [('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
Используем функцию methodcaller() для сортировки учеников по взвешенной оценке:
>>> [(student.name, student.weighted_grade()) for student in student_objects] [('john', 0.13333333333333333), ('jane', 0.08333333333333333), ('dave', 0.1)] >>> sorted(student_objects, key=methodcaller('weighted_grade')) [('jane', 'B', 12), ('dave', 'B', 10), ('john', 'A', 15)]
Сортировка по возрастанию и сортировка по убыванию в Python
list.sort() и sorted() есть параметр reverse , принимающий boolean-значение. Он нужен для обозначения сортировки по убыванию. Отсортируем учеников по убыванию возраста:
>>> sorted(student_tuples, key=itemgetter(2), reverse=True) [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)] >>> sorted(student_objects, key=attrgetter('age'), reverse=True) [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
Стабильность сортировки и сложные сортировки в Python
Начиная с версии Python 2.2, сортировки гарантированно стабильны: если у нескольких записей есть одинаковые ключи, их порядок останется прежним. Пример:
>>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)] >>> sorted(data, key=itemgetter(0)) [('blue', 1), ('blue', 2), ('red', 1), ('red', 2)]
Обратите внимание, что две записи с ‘blue’ сохранили начальный порядок. Это свойство позволяет составлять сложные сортировки путём постепенных сортировок. Далее мы сортируем данные учеников сначала по возрасту в порядке возрастания, а затем по оценкам в убывающем порядке, чтобы получить данные, отсортированные в первую очередь по оценке и во вторую — по возрасту:
>>> s = sorted(student_objects, key=attrgetter('age')) # сортируем по вторичному ключу >>> sorted(s, key=attrgetter('grade'), reverse=True) # по первичному [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
Алгоритмы сортировки Python вроде Timsort проводят множественные сортировки так эффективно, потому что может извлечь пользу из любого порядка, уже присутствующего в наборе данных.
Декорируем-сортируем-раздекорируем
- Сначала исходный список пополняется новыми значениями, контролирующими порядок сортировки.
- Затем новый список сортируется.
- После этого добавленные значения убираются, и в итоге остаётся отсортированный список, содержащий только исходные элементы.
Вот так можно отсортировать данные учеников по оценке:
>>> decorated = [(student.grade, i, student) for i, student in enumerate(student_objects)] >>> decorated.sort() >>> [student for grade, i, student in decorated] # раздекорируем [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
Это работает из-за того, что кортежи сравниваются лексикографически, сравниваются первые элементы, а если они совпадают, то сравниваются вторые и так далее.
Не всегда обязательно включать индекс в декорируемый список, но у него есть преимущества:
- Сортировка стабильна — если у двух элементов одинаковый ключ, то их порядок не изменится.
- У исходных элементов не обязательно должна быть возможность сравнения, так как порядок декорированных кортежей будет определяться максимум по первым двум элементам. Например, исходный список может содержать комплексные числа, которые нельзя сравнивать напрямую.
Ещё эта идиома называется преобразованием Шварца в честь Рэндела Шварца, который популяризировал её среди Perl-программистов.
Для больших списков и версий Python ниже 2.4, «декорируем-сортируем-раздекорируем» будет оптимальным способом сортировки. Для версий 2.4+ ту же функциональность предоставляют функции-ключи.
Использование параметра cmp
Все версии Python 2.x поддерживали параметр cmp для обработки пользовательских функций сравнения. В Python 3.0 от этого параметра полностью избавились. В Python 2.x в sort() можно было передать функцию, которая использовалась бы для сравнения элементов. Она должна принимать два аргумента и возвращать отрицательное значение для случая «меньше чем», положительное — для «больше чем» и ноль, если они равны:
>>> def numeric_compare(x, y): return x - y >>> sorted([5, 2, 4, 1, 3], cmp=numeric_compare) [1, 2, 3, 4, 5]
Можно сравнивать в обратном порядке:
>>> def reverse_numeric(x, y): return y - x >>> sorted([5, 2, 4, 1, 3], cmp=reverse_numeric) [5, 4, 3, 2, 1]
При портировании кода с версии 2.x на 3.x может возникнуть ситуация, когда нужно преобразовать пользовательскую функцию для сравнения в функцию-ключ. Следующая обёртка упрощает эту задачу по Python:
def cmp_to_key(mycmp): 'Перевести cmp=функция в key=функция' class K(object): def __init__(self, obj, *args): self.obj = obj def __lt__(self, other): return mycmp(self.obj, other.obj) < 0 def __gt__(self, other): return mycmp(self.obj, other.obj) >0 def __eq__(self, other): return mycmp(self.obj, other.obj) == 0 def __le__(self, other): return mycmp(self.obj, other.obj) = 0 def __ne__(self, other): return mycmp(self.obj, other.obj) != 0 return K
Чтобы произвести преобразование, оберните старую функцию:
>>> sorted([5, 2, 4, 1, 3], key=cmp_to_key(reverse_numeric)) [5, 4, 3, 2, 1]
В Python 2.7 функция cmp_to_key() была добавлена в модуль functools.
Поддержание порядка сортировки
В стандартной библиотеке Python нет модулей, аналогичных типам данных C++ вроде set и map . Python делегирует эти задачи сторонним библиотекам, доступным в Python Package Index: они используют различные методы для сохранения типов list , dict и set в отсортированном порядке. Поддержание порядка с помощью специальной структуры данных может помочь избежать очень медленного поведения (квадратичного времени выполнения) при наивном подходе с редактированием и постоянной пересортировкой данных. Вот некоторые из модулей, реализующих эти типы данных:
- SortedContainers — реализация сортированных типов list , dict и set на чистом Python, по скорости не уступает реализациям на C. Тестирование включает 100% покрытие кода и многие часы стресс-тестирования. В документации можно найти полный справочник по API, сравнение производительности и руководства по внесению своего вклада.
- rbtree — быстрая реализация на C для типов dict и set . Реализация использует структуру данных, известную как красно-чёрное дерево.
- treap — сортированный dict . В реализации используется Декартово дерево, а производительность улучшена с помощью Cython.
- bintrees — несколько реализаций типов dict и set на основе деревьев на C. Самые быстрые основаны на АВЛ и красно-чёрных деревьях. Расширяет общепринятый API для предоставления операций множеств для словарей.
- banyan — быстрая реализация dict и set на C.
- skiplistcollections — реализация на чистом Python, основанная на списках с пропусками, предлагает ограниченный API для типов dict и set .
- blist — предоставляет сортированные типы list , dict и set , основанные на типе данных «blist», реализация на Б-деревьях. Написано на Python и C.
Прочее
Для сортировки с учётом языка используйте locale.strxfrm() в качестве ключевой функции или locale.strcoll() в качестве функции сравнения. Параметр reverse всё ещё сохраняет стабильность сортировки. Этот эффект можно сымитировать без параметра, использовав встроенную функцию reversed() дважды:
>>> data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)] >>> assert sorted(data, reverse=True) == list(reversed(sorted(reversed(data))))
Чтобы создать стандартный порядок сортировки для класса, просто добавьте реализацию соответствующих методов сравнения:
>>> Student.__eq__ = lambda self, other: self.age == other.age >>> Student.__ne__ = lambda self, other: self.age != other.age >>> Student.__lt__ = lambda self, other: self.age < other.age >>> Student.__le__ = lambda self, other: self.age >> Student.__gt__ = lambda self, other: self.age > other.age >>> Student.__ge__ = lambda self, other: self.age >= other.age >>> sorted(student_objects) [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
Для типов, сравнение которых работает обычным образом, рекомендуется определять все 6 операторов. Декоратор классов functools.total_ordering упрощает их реализацию. Функциям-ключам не нужен доступ к внутренним данным сортируемых объектов. Они также могут осуществлять доступ к внешним ресурсам. Например, если оценки ученика хранятся в словаре, их можно использовать для сортировки отдельного списка с именами учеников:
>>> students = ['dave', 'john', 'jane'] >>> newgrades = >>> sorted(students, key=newgrades.__getitem__) ['jane', 'dave', 'john']
Надеемся, теория по Python list sort и соответствующие задачи по Питону с разбором были для вас полезны. Вас также может заинтересовать статьи:
- Хочу научиться программировать на Python. С чего начать?
- Хочу научиться программировать на Python: инструкция для продолжающих
Сортировка списков в Python
В Python данные можно сортировать с помощью методов sorted() или sort() . В этой статье мы поговорим о том, как работает сортировка списков в Python. Разберем примеры кода для методов sorted() и sort() и посмотрим, чем они отличаются.
Что такое метод sort() в Python?
Этот метод берет список и сортирует его. То есть на выходе мы получаем тот же список, только отсортированный. Этот метод не возвращает никакого значения.
В этом примере у нас есть список чисел, и мы можем использовать метод sort() для сортировки списка в порядке возрастания.
my_list = [67, 2, 999, 1, 15] # Выводим неупорядоченный список: print("Unordered list: ", my_list) # Сортировка списка my_list.sort() # Выводим упорядоченный список print("Ordered list: ", my_list)
Выполним наш код и получим следующий результат:
Unordered list: [67, 2, 999, 1, 15] Ordered list: [1, 2, 15, 67, 999]
Однако если список уже отсортирован, то мы получим None.
my_list = [6, 7, 8, 9, 10] # Это строка вернет None, потому что список уже отсортирован print(my_list.sort())
Метод sort() может принимать два необязательных аргумента: key и reverse .
Значением key выступает функция, которая будет вызываться для каждого элемента в списке.
От редакции Pythonist. О функциях и их аргументах у нас есть отдельная статья — «Функции и их аргументы в Python 3».
В следующем примере давайте используем функцию len() в качестве значения аргумента key. Таким образом, key=len скажет компьютеру отсортировать список имен по длине, от наименьшего к наибольшему.
names = ["Jessica", "Ben", "Carl", "Jackie", "Wendy"] print("Unsorted: ", names) names.sort(key=len) print("Sorted: ", names)
Вот, что мы получим:
Unsorted: ['Jessica', 'Ben', 'Carl', 'Jackie', 'Wendy'] Sorted: ['Ben', 'Carl', 'Wendy', 'Jackie', 'Jessica']
Аргумент reverse может иметь логическое значение: True (Истина) или False (Ложь).
В следующем примере reverse=True укажет компьютеру отсортировать список в обратном алфавитном порядке.
names = ["Jessica", "Ben", "Carl", "Jackie", "Wendy"] print("Unsorted: ", names) names.sort(reverse=True) print("Sorted: ", names) # Результат: # Unsorted: ['Jessica', 'Ben', 'Carl', 'Jackie', 'Wendy'] # Sorted: ['Wendy', 'Jessica', 'Jackie', 'Carl', 'Ben']
[python_ad_block]
Как использовать метод sorted() в Python
Этот метод превращает итерируемый объект в отсортированный список. Итерируемыми объектами могут быть списки, строки и кортежи.
Одно из ключевых различий между sort() и sorted() заключается в том, что sorted() вернет новый список, а sort() сортирует уже имеющийся.
В следующем примере у нас есть список чисел, который нужно отсортировать в порядке возрастания.
sorted_numbers = sorted([77, 22, 9, -6, 4000]) print("Sorted in ascending order: ", sorted_numbers) # Результат: # Sorted in ascending order: [-6, 9, 22, 77, 4000]
Метод sorted() тоже принимает необязательные аргументы. Они такие же, как и у sort() : key и reverse .
Давайте разберем следующий пример. У нас есть список чисел. Пропишем необязательный аргумент reverse=True . Он укажет компьютеру отсортировать список от наибольшего числа к наименьшему.
sorted_numbers = sorted([77, 22, 9, -6, 4000], reverse=True) print("Sorted in descending order: ", sorted_numbers) # Результат: # Sorted in descending order: [4000, 77, 22, 9, -6]
Метод sorted() для других типов данных
Еще одно ключевое различие между sorted() и sort() заключается в том, что метод sorted() принимает любые итерируемые объекты (списки, строки, кортежи и т.д.), тогда как метод sort() работает только со списками.
Давайте разобьём строку на отдельные слова с помощью метода split() , а затем используем метод sorted() для сортировки слов по длине от наименьшего к наибольшему.
my_sentence = "Jessica found a dollar on the ground" # Вывод оригинального предложения: print("Original sentence: ", my_sentence) # Вывод отсортированного списка слов: print(sorted(my_sentence.split(), key=len)) # Результат: # Original sentence: Jessica found a dollar on the ground # ['a', 'on', 'the', 'found', 'dollar', 'ground', 'Jessica']
А теперь давайте чуть изменим наш пример. Добавим необязательный аргумент reverse . Благодаря этому список будет отсортирован в обратном порядке, от самого длинного слова к самому короткому.
my_sentence = "Jessica found a dollar on the ground" print("Original sentence: ", my_sentence) print(sorted(my_sentence.split(), key=len, reverse=True)) # Результат: # Original sentence: Jessica found a dollar on the ground # ['Jessica', 'dollar', 'ground', 'found', 'the', 'on', 'a']
Мы также можем использовать метод sorted() и для кортежей.
В этом примере у нас есть набор кортежей, которые представляют имя, возраст и музыкальный инструмент учащихся какой-то группы.
band_students = [ ('Danny', 17, 'Trombone'), ('Mary', 14, 'Flute'), ('Josh', 15, 'Percussion') ]
Мы можем использовать метод sorted() для сортировки этих данных по возрасту учащегося. Аргумент key будет иметь значение лямбда-функции, которая сообщает компьютеру о сортировке по возрасту в порядке возрастания.
Лямбда-функция – это анонимная функция. Этот тип функции можно определить с помощью ключевого слова lambda .
Итак, напишем следующую лямбду-функцию, которая будет говорить нашей программе, что отсортировать мы хотим именно по возрасту:
lambda student: student[1]
Чтобы получить доступ к значению в кортеже, мы используем квадратные скобки [] и индекс элемента в кортеже, к которому хотим получить доступ. Поскольку в Python отсчет идет с нуля, возраст у нас будет под индексом [1] .
Таким образом, мы получаем следующий код:
band_students = [ ('Danny', 17, 'Trombone'), ('Mary', 14, 'Flute'), ('Josh', 15, 'Percussion') ] print(sorted(band_students, key=lambda student: student[1])) # Результат: # [('Mary', 14, 'Flute'), ('Josh', 15, 'Percussion'), ('Danny', 17, 'Trombone')]
Мы можем изменить этот пример. Давайте отсортируем кортежи по названиям музыкальных инструментов. Более того, давайте используем reverse=True для сортировки инструментов в обратном алфавитном порядке.
band_students = [ ('Danny', 17, 'Trombone'), ('Mary', 14, 'Flute'), ('Josh', 15, 'Percussion') ] print(sorted(band_students, key=lambda student: student[2], reverse=True)) # Результат: # [('Danny', 17, 'Trombone'), ('Josh', 15, 'Percussion'), ('Mary', 14, 'Flute')]
Заключение
В этой статье мы разобрали, как работает сортировка списков в Python. Узнали, как работать с такими методами, как sort() и sorted() , и в чем их различия.
Метод sort() работает только со списками и сортирует уже имеющийся список. Данный метод ничего не возвращает.
А метод sorted() работает с любыми итерируемыми объектами и возвращает новый отсортированный список. В качестве итерируемых объектов могут выступать списки, строки, кортежи и другие.
У обоих этих методов есть два необязательных аргумента: key и reverse .
Значением аргумента key может быть функция. Она будет вызываться для каждого элемента в списке. По этой функции, собственно, и будет проводиться сортировка.
Значением аргумента reverse может быть True или False .
Надеемся, эта статья была для вас полезна. Успехов в написании кода!
Как работает метод sort в python?
В python, метод sort применяется к спискам(в отличие от функции sorted() , которая применяется к любым итерируемым объектам). Важно, что все элементы списка должны быть одинакового типа(строки, числа, кортежи). Метод list.sort() изменяет список по месту, и возвращает None . Если вызвать метод без параметров, то элементы списка отсортируются в порядке возрастания.
my_list = [7, 5, 8, 2, 11, 1, 14] my_list.sort() print(my_list) # [1, 2, 5, 7, 8, 11, 14]
Сортировка в порядке убывания:
my_list = [7, 5, 8, 2, 11, 1, 14] my_list.sort(reverse=True) print(my_list) # [14, 11, 8, 7, 5, 2, 1]
Также, метод sort может принимать параметр key (функция), по которому будет произведена сортировка:
my_list = [7, 5, 8, 2, 11, 1, 14] my_list.sort(key=lambda x: x%2)
в данном случае, сначала в отсортированном списке буду четные, а потом нечетные элементы.