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

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

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

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

>>> 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]))

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

Далее определяется функция 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