Сортировка по произвольным элементам вложенных списков

В Python можно выполнить сортировку списка на месте с помощью метода sort():

>>> a = [10,3,4,1,9]
>>> a.sort()
>>> a
[1, 3, 4, 9, 10]

Отметим, что есть аналогичная списковому методу sort() встроенная функция sorted(), которая не изменяет последовательность, а возвращает новую отсортированную.

Если элементы списка сами представляют собой списки, т. е. являются вложенными списками, то сортировка будет происходить по первым элементам вложенных списков, то есть в случае матрицы по первому столбцу:

>>> a = [[12,101],[2,200],[18,99]]
>>> a.sort()
>>> a
[[2, 200], [12, 101], [18, 99]]

Что делать, если надо отсортировать не по первому столбцу? На этот случай sort() принимает необязательный аргумент key, в котором передается другая функция. Этой другой функции передается очередной элемент списка. Она может сделать с ним что угодно и вернуть что угодно. По этому "что угодно" и происходит сортировка.

Так, например, пользовательская функция может возвращать из переданного ей элемента, представляющего собой вложенный список, любой элемент этого вложенного списка. В свою очередь функция sort() будет сортировать по тем значениям, которые ей возвращаются.

В качестве примера приведем программу, в которой список представляет собой маленькую базу данных. Допустим, каждый элемент содержит сведения о юном спортсмене: имя, возраст, рост и вес. Пользователь может заказать сортировку по любому полю:

a = [['петя',10,130,35], ['вася',11,135,39],
['женя',9,140,33],['дима',10,128,30]]

n = input('Сортировать по имени (1),
возрасту (2), росту (3), весу (4): ')
n = int(n)-1

def sort_col(i):
    return i[n]

a.sort(key=sort_col)

for i in a:
    print("%7s %3d %4d %3d" % (i[0],i[1],i[2],i[3]))

Здесь пользователь вводит номер поля. Число приводится к типу int, и из него вычитается единица, т. к. индексация списка начинается с нуля.

Далее определяется функция sort_col(). Ей передается аргумент i, а она возвращает n-ый элемент этого аргумента. Так, если этой функции передать вложенный список, то она вернет его n-й элемент. В данном случае тот, который хотел пользователь.

В функции sort() указывается пользовательская функция. Когда sort() извлекает очередной элемент списка, в данном случае - вложенный список, то передает этой функции. Получается, что элемент списка подменяется на то, что возвращает пользовательская функция.

В данном случае если пользователь заказывает сортировку по второму столбцу, вывод будет таким:

Сортировать по имени (1), возрасту (2),
росту (3), весу (4): 2
   женя   9  140  33
   петя  10  130  35
   дима  10  128  30
   вася  11  135  39

Можно не определять обычную функцию, а использовать lambda-функцию:

a = [['петя',10,130,35], ['вася',11,135,39],
['женя',9,140,33],['дима',10,128,30]]

n = input('Сортировать по имени (1),
возрасту (2), росту (3), весу (4): ')
n = int(n)-1

a.sort(key=lambda i: i[n])

for i in a:
    print("%7s %3d %4d %3d"
    % (i[0],i[1],i[2],i[3]))

Кроме того, метод sort() имеет еще один необязательный параметр по ключевому слову - reverse. По умолчанию он равен False. Это значит, что сортировка происходит по возрастанию. Однако если у reverse будет значение True, то сортировка будет обратной, т. е. по убыванию. В измененной программе ниже реализована возможность выбора типа сортировки:

a = [['петя',10,130,35], ['вася',11,135,39],
['женя',9,140,33],['дима',10,128,30]]

n = input('Сортировать по имени (1),
возрасту (2), росту (3), весу (4): ')
n = int(n)-1
t = input('По возрастанию (0), по убыванию (1): ')
t = int(t)

a.sort(key=lambda i: i[n], reverse=t)

for i in a:
    print("%7s %3d %4d %3d"
    % (i[0],i[1],i[2],i[3]))

При сортировке по весу по убыванию получим:

Сортировать по имени (1), возрасту (2),
росту (3), весу (4): 4
По возрастанию (0), по убыванию (1): 1
   вася  11  135  39
   петя  10  130  35
   женя   9  140  33
   дима  10  128  30