Если необходимо отсортировать список по определенному критерию, используется функция sorted(). По стандарту, она настроена так, чтобы сортировать список по возрастанию, но пользователь может выставить другой параметр – убыванию или по функции, которая прописывается в качестве ключа. Рассмотрим более подробно, как осуществляется сортировка с помощью этой функции.
Когда применяется функция sorted()?
Сортировка данных может выполняться для самых разных задач. Независимо от того, для какой цели используется эта функция, она может принимать в качестве аргумента списки, словари и кортежи. Таким образом, эта функция будет невероятно полезной в случае, если есть большой массив неупорядоченной информации. А спектр применения поистине широк. По сути, работа с любой базой данных не обходится без нее.
В качестве ориентира для сортировки данных используются значения с ASCII-таблицы. Функция имеет следующие характеристики:
- Возвращает список упорядоченных элементов.
- В синтаксисе три параметра – итерируемый объект (то есть, тот набор данных, который упорядочивается), ключ-функция (используется для задания критерия для сортировки) и reverse, которая меняет последовательность сортировки (например, с возрастания на убывание).
- Последние два аргумента передавать функции необязательно. Если они не передаются, то сортировка по умолчанию производится по возрастанию.
Параметр key
Теперь следует отметить параметр key, который поможет сделать сортировку полностью персонализированной и очень гибкой. В качестве ключа могут выступать:
- Предустановленные функции в Python.
- Функции, которые разработал сам пользователь.
- Лямбда-функция.
- itemgetter.
- attrgetter.
Рассмотрим более подробно, какие бывают ключи.
Встроенная функция
Встроенные функции могут несколько изменять свое значение, если передаются в качестве аргумента key.
len(). По стандарту предназначена для расчета длины объекта. Если же его использовать в качестве ключа для сортировки, то итерируемый объект будет отсортирован по его длине.
Внимание: если используется словарь, то сортировка будет осуществляться исключительно по значениям.
Пример:
l1 = {‘carrot’: ‘vegetable’, ‘red’: ‘color’, ‘apple’: ‘fruit’}
print(sorted(l1, key=len))
# Вывод: [‘red’, ‘apple’, ‘carrot’]
abs(). Эта функция возвращает модуль числа (то есть, убирает минус во всех отрицательных числах). Соответственно, если использовать эту функцию в качестве ключа, то основой для сортировки будут модули всех чисел по возрастанию или же, если задается параметр reverse, по убыванию.
l1 = [1, -4, 5, -7, 9, 2]
print(sorted(l1, key=abs))
# Вывод: [1, 2, -4, 5, -7, 9]
str.lower(). Выполняет конвертацию символов, которые находятся в верхнем регистре, в нижний. Соответственно, сортировка списка будет выполняться так, как будто все символы находятся в нижнем регистре (то есть, он не будет учитываться при сортировке, будет простая алфавитная сортировка).
Приведем пример для наглядности.
s1 = «Hello How are you»
print(sorted(s1.split(), key=str.lower))
# Вывод: [‘are’, ‘Hello’, ‘How’, ‘you’]
Пользовательские функции
Добавление своих функций также возможно. Приведем несколько примеров для наглядности.
Пример 1. В кортежах элементы по стандарту сортируются, исходя из того, какой первый элемент. Тем не менее, можно осуществить сортировку по второму элементу кортежа. Для этого необходимо объявить функцию, которая будет возвращать его.
# напишем функцию для получения второго элемента
def sort_key(e):
return e[1]
l1 = [(1, 2, 3), (2, 1, 3), (11, 4, 2), (9, 1, 3)]
# По умолчанию сортировка выполняется по первому элементу
print(sorted(l1))
# Вывод: [(1, 2, 3), (2, 1, 3), (9, 1, 3), (11, 4, 2)]
# Сортировка по второму элементу с помощью функции sort_key
print(sorted(l1, key=sort_key))
# Вывод: [(2, 1, 3), (9, 1, 3), (1, 2, 3), (11, 4, 2)]
Пример 2. С помощью функций можно выполнять также сортировку объектов класса. Это осуществляется таким образом. Предположим. у нас есть класс Student. Он имеет три атрибута – name, rollno, grade. После этого мы создаем три объекта, которые относятся к классу и добавляем их в список. После этого выполняем сортировку.
Затем объявляется функция sort_key, которая настроена на возвращение атрибута rollno. Соответственно, весь список будет сортироваться по этому значению.
Приведем пример программы, наглядно демонстрирующей, как это работает.
class Student:
def __init__(self, name, rollno, grade):
self.name = name
self.rollno = rollno
self.grade = grade
def __repr__(self):
return f»{self.name}-{self.rollno}-{self.grade}»
# Создание объектов
s1 = Student(«Paul», 15, «second»)
s2 = Student(«Alex», 12, «fifth»)
s3 = Student(«Eva», 21, «first»)
s4 = [s1, s2, s3]
# Сортировка списка объектов
# Создание функции, которая вернет rollno объекта
def sort_key(s):
return s.rollno
# сортировка списка объектов без ключевого параметра, вызывает TypeError
print(sorted(s4))
# Вывод: TypeError: ‘>’ not supported between instances of ‘Student’ and ‘Student’
# Сортировка списка объектов по атрибуту: rollno
s5 = sorted(s4, key=sort_key)
print(s5)
# Вывод: [Alex-12-fifth, Paul-15-second, Eva-21-first]
Лямбда-функция
Лямбда функция – это особенный синтаксис, который позволяет в одной строчке и создать функцию и передать ее. Допустим, у нас есть такой код, и нам его надо упростить.
colors = [«Goldenrod», «Purple», «Salmon», «Turquoise», «Cyan»])
def normalize_case(string):
return string.casefold()
normalized_colors = map(normalize_case, colors)
После того, как мы используем лямбда-функцию, код станет таким.
colors = [«Goldenrod», «Purple», «Salmon», «Turquoise», «Cyan»])
normalized_colors = map(lambda s: s.casefold(), colors)
Такие функции используются для тех ситуаций, когда не нужно использовать переменную, чтобы их передать. Типичный пример – сортировка. Теперь рассмотрим несколько практических примеров использования лямбда-функций.
Пример 1. Лямбда-функция используется для того, чтобы отсортировать список объектов.
class Student:
def __init__(self, name, rollno, grade):
self.name = name
self.rollno = rollno
self.grade = grade
def __repr__(self):
return f»{self.name}-{self.rollno}-{self.grade}»
# Создание объектов
s1 = Student(«Paul», 15, «second»)
s2 = Student(«Alex», 12, «fifth»)
s3 = Student(«Eva», 21, «first»)
s4 = [s1, s2, s3]
# Сортировка списка объектов
# Создание lambda-функции, которая вернет rollno объекта
s5 = sorted(s4, key=lambda x: x.rollno)
print(s5)
# Вывод: [Alex-12-fifth, Paul-15-second, Eva-21-first]
Пример 2. Использование лямбда-функции для возврата второго объекта кортежа и дальнейшей его сортировки по нему.
l1 = [( 1, 2, 3), (3, 1, 1), (8, 5, 3), (3, 4, 2)]
# Сортировка по второму элементу в кортеже.
# Lambda-функция возвращает второй элемент в кортеже.
print(sorted(l1, key=lambda x: x[1]))
# Вывод: [(3, 1, 1), (1, 2, 3), (3, 4, 2), (8, 5, 3)]
Тем не менее, специалисты рекомендуют применять лямбда-функции не очень часто. Несмотря на их внешнюю простоту, они значительно ухудшают читабельность кода в случае их чрезмерного использования.
В частности, не нужно использовать лямбда-функции, если уже есть объявленные или существующие функции, выполняющие те же действия.
itemgetter
С помощью itemgetter можно получить несколько объектов (список, кортеж или множество) и вернуть конкретный его элемент.
Давайте попробуем в качестве ключа использовать третий элемент каждого кортежа. Для этого используем itemgetter с индексом 2 (ведь первый элемент считается 0).
l1 = [( 1, 2, 3), (3, 1, 1), (8, 5, 3), (3, 4, 2)]
# Сортировка по второму элементу в кортеже.
# Lambda-функция возвращает второй элемент в кортеже.
print(sorted(l1, key=lambda x: x[1]))
# Вывод: [(3, 1, 1), (1, 2, 3), (3, 4, 2), (8, 5, 3)]
Обратите внимание, что мы импортировали itemgetter из модуля operator.
attrgetter
Позволяет получить кортеж атрибутов или один атрибут и осуществить сортировку по ним.
from operator import attrgetter
class Student:
def __init__(self, name, rollno, grade):
self.name = name
self.rollno = rollno
self.grade = grade
def __repr__(self):
return f»{self.name}-{self.rollno}-{self.grade}»
# Создание объектов
s1 = Student(«Paul», 15, «second»)
s2 = Student(«Alex», 12, «fifth»)
s3 = Student(«Eva», 21, «first»)
s4 = [s1, s2, s3]
# Сортировка списка объектов по атрибуту: rollno
s5 = sorted(s4, key=attrgetter(‘rollno’))
print(s5)
# Вывод: [Alex-12-fifth, Paul-15-second, Eva-21-first]
Многоуровневая сортировка
itemgetter также позволяет сортировать по разному количеству элементов на каждом этапе. Например, сначала выполнить сортировку по второму. В случае равенства некоторых объектов – выполнить сортировку по третьему элементу.
from operator import itemgetter
d = {‘a’: [1, 2, 9], ‘b’: [3, 2, 5], ‘c’: [1, 1, 2]}
# Сортировка по второму, затем по третьему элементу
print(sorted(d.values(), key=itemgetter(1, 2)))
# Вывод: [[1, 1, 2], [3, 2, 5], [1, 2, 9]]
Многоуровневая сортировка может также выполняться с использованием лямбда-функции.
Сортировка смешанных типов данных
Частый вопрос: поддерживается ли сортировка смешанных типов данных. К сожалению, нет. Если попробовать отсортировать их, будет выдано исключение TypeError.
Таким образом, сортировка осуществляется по определенному ключу. Следовательно, этот аргумент обязательно передавать функции. В качестве ключа может использоваться любая функция, в том числе, и определенная пользователем. Объект, возвращаемый функцией sorted() – список.