Сортировка списков в Python — метод sort и функция sorted
В Python для сортировки списков предусмотрен метод sort, также можно использовать встроенную в Python функцию sorted. Основная разница между ними заключается в том, что метод списка сортирует сам список, к которому применяется (говорят, сортировка происходит на месте), а функция sorted возвращает новый список, оставляя оригинал неизменным.
>>> a = [1, -2, 0] >>> b = [10, -20, 3] >>> a.sort() >>> c = sorted(b) >>> a [-2, 0, 1] >>> b [10, -20, 3] >>> c [-20, 3, 10]
Как sort, так и sorted принимают аргумент True или False по ключу reverse. Значение False задано по-умолчанию, поэтому сортировка происходит от меньшего к большему, то есть по возрастанию. Для обратной сортировки, то есть по убыванию, параметру reverse следует присвоить значение True.
>>> a = [4, 2, 5, 3] >>> a.sort(reverse=True) >>> a [5, 4, 3, 2]
Кроме этого sort и sorted имеют параметр key, которому присваивается функция. Допустим, у нас есть матрица, которую мы хотим отсортировать:
>>> a = [[1, 10], [0, 15], [-1, 12], [3, 9]] >>> a.sort() >>> a [[-1, 12], [0, 15], [1, 10], [3, 9]]
По-умолчанию сортировка выполняется по первым элементам вложенных списков, то есть в случае матрицы по первому столбцу. Чтобы изменить принцип сортировки, надо в метод sort() передать значение для параметра key. Этим значением должна быть другая функция, которая будет автоматически вызываться на каждый очередной элемент списка (он будет в нее передаваться). Она может сделать с ним что угодно и вернуть что угодно. По этому "что угодно" и происходит сортировка.
Так, например, пользовательская функция может возвращать из переданного ей элемента, представляющего собой вложенный список, любой элемент этого вложенного списка. В свою очередь функция sort() будет сортировать по тем значениям, которые ей возвращаются.
Конечно, мы можем определить обычную функцию (кстати, в Python таковая, если содержит всего одно выражение в теле, может быть записана в одну строку):
>>> def num_item(i): return i[1] >>> a.sort(key=num_item) >>> a [[3, 9], [1, 10], [-1, 12], [0, 15]]
Однако будет проще с lambda-выражением:
>>> a.sort() >>> a [[-1, 12], [0, 15], [1, 10], [3, 9]] >>> a.sort(key=lambda i: i[1]) >>> a [[3, 9], [1, 10], [-1, 12], [0, 15]]
При этом key не обязательно используется только при наличии вложенных списков.
>>> a = [1, 2, 3, 4, 5, 6] >>> sorted(a, key=lambda i: i%2) [2, 4, 6, 1, 3, 5]
Здесь мы отсортировали так, что сначала идут четные элементы, потом нечетные.
В результирующий список помещаются элементы исходного списка. Однако сортировка происходит не в зависимости от их непосредственного значения, а в зависимости от того, что возвращает функция, переданная key, примененная по отношению к каждому элементу.
В примере выше, если применить lambda-функцию к каждому элементу списка-оригинала, то получится такой список-маска: [1, 0, 1, 0, 1, 0]. Он и будет отсортирован. Однако в список-результат вместо значений из маски будут подставлены связанные с ними значения из списка-оригинала.
Тот факт, что у нас внутри себя отсортированы как четные (2, 4, 6), так и нечетные (1, 3, 5) числа, никак не связан с выполненной сортировкой. Это связано только с тем, как элементы были заданы в исходном списке. Сравните:
>>> a = [6, 4, 1, 5, 3, 2] >>> sorted(a, key=lambda i: i%2) [6, 4, 2, 1, 5, 3]
Параметру key можно присвоить и встроенную в Python функцию.
>>> s = ['bb', 'd', 'cccc', 'aaa'] >>> sorted(s) ['aaa', 'bb', 'cccc', 'd'] >>> sorted(s, key=len) ['d', 'bb', 'aaa', 'cccc'] >>> sorted(s, key=len, reverse=True) ['cccc', 'aaa', 'bb', 'd']
Здесь мы сортируем строки, которые по-умолчанию сортируются лексикографически. Однако используя встроенную функцию len, которая в данном случае возвращает длину строки, мы можем сортировать в зависимости от длины строк.
Рассмотрим пример с обычной функцией. В программе ниже список представляет собой маленькую базу данных. Каждый элемент содержит сведения о юном спортсмене: имя, возраст, рост и вес. Пользователь может заказать сортировку по любому полю. Мы сортируем с помощью функции sorted, чтобы исходный список оставался неизменным.
def sort_col(item): return item[int(n)] a = [['Петя', 10, 130, 35], ['Вася', 11, 135, 39], ['Женя', 9, 120, 33], ['Дима', 10, 128, 30]] n = input('Сортировать по имени(0), ' 'возрасту(1), росту(2), весу(3): ') b = sorted(a, key=sort_col) for i in b: print('%7s %3d %4d %3d' % (i[0], i[1], i[2], i[3]))
Если выбрать сортировку по весу, вывод будет таким:
Сортировать по имени(0), возрасту(1), росту(2), весу(3): 3 Дима 10 128 30 Женя 9 120 33 Петя 10 130 35 Вася 11 135 39
Отдельно посмотрим на определение функции sort_col:
def sort_col(item): return item[int(n)]
Она принимает некий объект item, извлекает из него элемент под номером n и возвращает этот элемент.
При использовании sort_col в качестве значения параметра key функции sorted происходит вызов sort_col по отношению к каждому элементу списка. В данном случае элементом списка является вложенный список условного формата [имя, возраст, рост, вес], он передается в функцию. Функция sort_col извлекает из него элемент под заданным номером n и передает этот элемент функции sorted. В свою очередь sorted сортирует элементы исходного списка, исходя из соответствующих значений, которые вернул sort_col.
Здесь вместо обычной можно использовать lambda-функцию:
b = sorted(a, key=lambda item: item[int(n)])
Однако если требуются более сложная обработка данных без полноценной функции не обойтись.
Практическая работа
- Доработайте приведенную в уроке программу про юных спортсменов таким образом, чтобы пользователь выбирал не только столбец сортировки, также мог указать тип сортировки – по возрастанию или убыванию.
- Дан список строк. Отсортируйте его лексикографически по последним буквам слов.