[МУЗЫКА] [МУЗЫКА] Итак, напомню: мы остановились на задаче упорядочить людей по убыванию роста, а в случае, если рост у них одинаковый, то по алфавиту. Воспользоваться reverse мы попробовали, но не получилось. Нам придется придумать что-нибудь, что позволит нам упорядочить этих людей ровно так, как мы задумали. Для этого можно произвести некоторую модификацию, а именно: мы рост человека заменим на такое же по модулю отрицательное число, то есть у нас люди теперь будут отрицательного роста. И вот человек ростом 1 метр 80 превратится в человека с ростом −180, а человек ростом 1 метр 72 превратится в человека ростом, соответственно, −172 сантиметра. И тогда, если мы упорядочим их по возрастанию обычным образом, то у нас люди более высокого роста изначально, окажутся раньше, потому что теперь у них стал очень отрицательный рост, очень маленький. Потом, конечно, данные нужно вернуть в тот вид, как они и были. Как это можно сделать? Сделать это можно примерно следующим образом. Например, давайте исходные данные, поскольку они у нас константные, возьмем и сразу исправим. Если бы данные считывались с клавиатуры, как это обычно и бывает, можно было считать число, определяющее рост человека, отдельно, и в переменную уже, в кортеж, класть не это число, а «минус» это число. Итак, давайте посмотрим, что у нас пока что все работает. Да, теперь смотрите, у нас люди отсортировались. Если взять модуль их роста, они отсортированы правильно — сначала идет самый высокий человек. И в алфавитном порядке тоже правильно. Осталось избавиться от этих минусов, которые мы придумали, чтобы правильно упорядочить людей. То есть мы модифицировали данные, но нужно все-таки возвращать, как было. Как это можно сделать? Можно пройтись по всем элементам нашего списка. Поскольку у нас элемент списка кортеж неизменяемый, нам придется конструировать новый кортеж, то есть p[i] — очередной элемент списка заменять на кортеж, состоящий из p[i] [0] — это отрицательный рост человека, Что мы хотим? «Минус» его взять. Если мы возьмем минус от минуса, получится правильный рост. И p[i] [1], имя человека, здесь без изменений. И только после такой операции восстановления данных, как они были, мы можем печатать список. Поехали. Всё, отлично. Люди упорядочились по убыванию по невозрастанию роста, а в случае одинакового роста — по алфавиту. Что здесь произошло? Мы взяли исходные данные — рост, имя; испортили их — не испортили, модифицировали, для того, чтобы нам было удобно сортировать; заменили рост на соответствующее отрицательное число, отсортировали; а потом восстановили все, как было, то есть еще раз посчитали «минус»для каждого роста, чтобы у нас данные были в том же виде, как они были заданы. Это не очень удобно: каждый раз испортить данные, отсортировать, потом восстановить, как было. И поэтому в Python существует способ решать эту проблему по-другому. Давайте посмотрим сначала на примере более простой задачи: отсортировать строки, хранящие списки по длине, то есть по возрастанию длины этой строки. Я опять же создам какой-нибудь константный списочек, чтобы не писать длинный ввод. У меня есть три строки, напомню, если бы они были отсортированы, как строки, то они уже располагались бы в правильном порядке. Но нам нужно упорядочить их по возрастанию, по неубыванию длины. Что для этого делается? Мы вызываем метод sort и делаем ему именованный параметр key. key — ключ. Что это означает? Это функция, которая будет применяться к каждому объекту списка и считать от него некоторое значение, по этим значениям уже будет происходить сравнение. Если мы писали: key = len, то у нас будет вызываться функция len для подсчета длины строки. То есть для каждого элемента списка будет вызвана функция len, запомнится то значение, которое она вернула, и по этим значениям уже все отсортируется. Давайте посмотрим, что у нас получается. И действительно, строки отсортировались, начиная с самой короткой, потом средняя и потом самая длинная. А что было бы, если бы у нас были строки одинаковой длины? Давайте посмотрим, мы такого еще не делали. Они упорядочились по-прежнему по неубыванию длины, но обратите внимание на одну вещь: та строка из четырех символов, которая стояла раньше в исходном списке, также оказалась раньше, чем строка опять же из четырех символов, которая стояла позже. То есть взаимный порядок одинаковых по ключу элементов сохранился. Это свойство называется устойчивость сортировки, и в Python стандартная сортировка устойчива. То есть если у вас ключи для каких-то объектов были одинаковые, то они расположатся точно так же, как стояли в исходном списке. Теперь давайте посмотрим еще один пример на использование key. Это сортировка точек по возрастанию расстояния от начала координат до этой точки. Создадим списочек с точками какими-то, например, кортеж x, y координата. [БЕЗ СЛОВ] И мы хотим эти точки теперь отсортировать. Вызовем метод sort, в качестве ключа будем передавать функцию, которая считает расстояние от начала координат до этой точки. Есть функция библиотеки math для подсчета длины гипотенузы, если известны катеты, но, в принципе, мы можем обойтись и без нее. Почему? Потому что все координаты наших точек целочисленны. Когда мы пользуемся функцией подсчета гипотенузы в треугольнике, у нас получается вещественное число, а вещественные числа имеют массу ограничений. Например, если координаты были очень большими, то у вас вещественное число переполнится, и точки отсортируются неправильно. Из математики нам известно, что выполняется такое свойство, что если у нас два числа как-то сравнимы между собой, a < b, и оба положительны, расстояние до точек положительно, то a² < b². То есть мы можем применить функцию, которая будет считать квадрат расстояния. В качестве key мы пишем просто имя нашей функции, и теперь давайте напишем саму нашу функцию. Что она принимает на вход? Она принимает на вход один элемент списка. В нашем случае это будет кортеж из двух целых чисел. Это будет point, и возвращать она должна квадрат x-координаты, это нулевой элемент кортежа, плюс квадрат y-координаты. Всё, теперь мы ни в какую вещественную арифметику не вылезем, потому что у нас считаются суммы квадратов целых чисел, и естественно, это целое число. И точки упорядочены по увеличению расстояния от начала координат. Что теперь можно с этим сделать? В принципе, теперь вы можете придумывать способ сравнения, писать свои функции и не портить данные. Давайте вернемся к задаче, которая у нас была, давайте я попробую откатиться к тому состоянию. Напомню, это задача про сортировку людей по убыванию, по невозрастанию роста. При этом мы не хотим портить наши данные, то есть вот они, наши красивые данные — настоящий рост людей, их настоящие имена. Нужно придумать какую-то функцию, которая будет возвращать измененный кортеж, так, как мы уже придумывали с отрицательным ростом для нормального кортежа. Я ей дал плохое название, совершенно непонятное, но у меня пока не работает фантазия, я надеюсь, что у вас будет получаться лучше. Итак, мы получаем описание человека и должны вернуть кортеж, по которому будет происходить сравнение. Нам передается один человек в эту функцию, то есть кортеж, состоящий из числа, роста и имени. Мы должны возвращать опять же кортеж с отрицательным ростом, чтобы у нас высокие люди оказались раньше, с меньшим ключом, чем более низкие люди; и имя в качестве второго элемента кортежа, чтобы в случае, если рост одинаковый, люди упорядочились по имени. И давайте посмотрим, что это у нас работает. Да, люди упорядочились по невозрастанию роста, и имена тоже в правильном порядке стоят для людей одинакового роста. Таким образом, с помощью параметра key мы можем передавать функцию, модифицирующую данные, и при этом не заниматься таким странным делом, как испортить данные, отсортировать и восстановить данные, как было. Они на самом деле не портятся, просто от каждого элемента считается key. Функция, которая возвращает ключ сравнения, может возвращать что угодно сравнимое: строку, число, вещественное число, кортеж, в том числе кортеж сложной структуры. Главное, чтобы эти объекты можно было сравнить между собой, понять, какой из них меньше, какой из них больше. Мы посмотрели несколько примеров, и я думаю, вы справитесь с теми задачами, которые мы предлагаем. [МУЗЫКА] [МУЗЫКА]