Сортировка с lambda: гибкие ключи и применение

Раздел: Инструменты -> Сортировка

Сортировка данных является одной из самых частых задач программирования. В Python для сортировки применяются функции sorted() и метод list.sort(). Параметр key позволяет задать функцию, возвращающую значение для сравнения. Lambda функции удобны для компактного определения такого ключа. Ниже рассматриваются различные ситуации использования lambda в сортировке.

Основные варианты сортировки с lambda

Наиболее эффективное решение - сортировка списка по одному из элементов вложенной структуры, например, по второму элементу кортежа. Этот подход повсеместен и прост.

data = [('apple', 2), ('banana', 1), ('cherry', 3)]
sorted_data = sorted(data, key=lambda x: x[1])
print(sorted_data)

Python sort key (сортировка с параметром key в python)

[('banana', 1), ('apple', 2), ('cherry', 3)]

Python sort lambda (сортировка с lambda в python)

Аналогично для сортировки на месте:

data.sort(key=lambda x: x[1])
print(data)

сортировка по алфавиту python (сортировка по алфавиту в python)

[('banana', 1), ('apple', 2), ('cherry', 3)]

Python числа в порядке возрастания (сортировка чисел по возрастанию в python)

Типичная проблема: если сравниваемые элементы имеют несовместимые типы (например, строка и число), возникает исключение TypeError. Решение: привести все значения к одному типу внутри lambda, например, key=lambda x: int(x[1]) если изначально строка.

Как отсортировать список словарей по значению определенного поля?

Для сортировки списка словарей по значению ключа используется lambda, которая извлекает значение по этому ключу:

users = [{'name': 'Anna', 'age': 25}, {'name': 'Ben', 'age': 20}, {'name': 'Cate', 'age': 30}]
sorted_users = sorted(users, key=lambda d: d['age'])
print(sorted_users)

Python сортировка по значению (сортировка по значению в python)

[{'name': 'Ben', 'age': 20}, {'name': 'Anna', 'age': 25}, {'name': 'Cate', 'age': 30}]

Ошибка: если в словаре нет указанного ключа, возникает KeyError. Решение: использовать метод .get() с значением по умолчанию, например key=lambda d: d.get('age', 0).

Как отсортировать экземпляры пользовательского класса по атрибуту?

Допустим, имеется класс Person с атрибутами name и age. Сортировка выполняется по атрибуту age:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __repr__(self):
        return f'{self.name}({self.age})'

persons = [Person('Anna', 25), Person('Ben', 20), Person('Cate', 30)]
sorted_persons = sorted(persons, key=lambda p: p.age)
print(sorted_persons)
[Ben(20), Anna(25), Cate(30)]

Проблема: если атрибут отсутствует, возникнет AttributeError. Решение: использовать getattr(p, 'age', 0) или убедиться, что атрибут существует.

Как отсортировать по нескольким ключам?

Для сортировки сначала по одному полю, затем по другому, ключ формируется в виде кортежа:

data = [(2, 3), (1, 2), (2, 1), (1, 1)]
sorted_data = sorted(data, key=lambda x: (x[0], x[1]))
print(sorted_data)
[(1, 1), (1, 2), (2, 1), (2, 3)]

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

sorted_data = sorted(data, key=lambda x: (x[0], -x[1]))
print(sorted_data)
[(1, 2), (1, 1), (2, 3), (2, 1)]

Внимание: порядок элементов в кортеже определяет приоритет ключей. Если один из ключей не число и не имеет обратного, для изменения порядка следует использовать reverse=True или functools.cmp_to_key.

Как отсортировать в обратном порядке?

Параметр reverse=True применяется после вычисления ключа:

sorted_data = sorted(data, key=lambda x: x[1], reverse=True)

Это эквивалентно key=lambda x: -x[1] если ключ числовой, но работает для любых типов.

Ошибка: забыть, что reverse меняет порядок всего результата, а не отдельного ключа. Для комбинированной сортировки лучше использовать кортеж с отрицательными значениями.

Как отсортировать строки без учета регистра?

Приведение к единому регистру с помощью lower() или upper():

words = ['Apple', 'banana', 'Cherry']
sorted_words = sorted(words, key=lambda s: s.lower())
print(sorted_words)
['Apple', 'banana', 'Cherry']

Проблема: если строка может быть None, вызов .lower() приведет к ошибке. Решение: добавить проверку: key=lambda s: (s is None, s.lower() if s else '').

Как отсортировать строки по длине?

Используется встроенная функция len или lambda:

words = ['apple', 'banana', 'cherry', 'date']
sorted_words = sorted(words, key=len)  # или key=lambda s: len(s)
print(sorted_words)
['date', 'apple', 'cherry', 'banana']

То же самое с обратным порядком (от самых длинных):

sorted_words = sorted(words, key=len, reverse=True)

Как отсортировать числа по модулю?

Функция abs в качестве ключа:

numbers = [3, -1, 2, -4, 0]
sorted_numbers = sorted(numbers, key=abs)
print(sorted_numbers)
[0, -1, 2, 3, -4]

Замечание: abs корректно работает с целыми и вещественными числами. Для комплексных чисел сравнение может быть неоднозначным.

Как реализовать нестандартное правило сравнения с помощью cmp_to_key?

В Python 3 функция cmp не используется напрямую. С помощью functools.cmp_to_key можно преобразовать компаратор в ключ. Например, сортировка по убыванию, но четные числа должны быть перед нечетными:

from functools import cmp_to_key
def compare(a, b):
    a_type = a % 2
    b_type = b % 2
    if a_type != b_type:
        return a_type - b_type
    return b - a
nums = [3, 8, 5, 2, 9, 4]
sorted_nums = sorted(nums, key=cmp_to_key(compare))
print(sorted_nums)
[8, 4, 2, 9, 5, 3]

Недостаток: cmp_to_key работает медленнее, чем обычный ключ. По возможности следует переписать правило в виде функции key, например, key=lambda x: (x % 2, -x).

Как заменить lambda на itemgetter или attrgetter?

Для сортировки по индексу или атрибуту часто удобнее использовать функции из модуля operator:

from operator import itemgetter, attrgetter
data = [(2, 'b'), (1, 'a'), (3, 'c')]
sorted(data, key=itemgetter(1))  # по второму элементу
# для объектов: sorted(persons, key=attrgetter('age'))

Lambda функции остаются более гибкими, когда нужно преобразование значений.

Расширенные примеры сортировки с lambda

Пример 1. Сортировка списка файлов по дате модификации с использованием lambda и os.path.getmtime:

Пример
import os
files = ['file1.txt', 'file2.txt', 'file3.txt']
# Предположим, файлы существуют; сортировка по времени последнего изменения
sorted_files = sorted(files, key=lambda f: os.path.getmtime(f))
print(sorted_files)
['file2.txt', 'file3.txt', 'file1.txt'] (зависит от файлов)

Пример 2. Сортировка строк по количеству гласных:

Пример
words = ['python', 'lambda', 'function', 'sort']
def count_vowels(s):
    return sum(1 for c in s.lower() if c in 'aeiou')
sorted_words = sorted(words, key=lambda s: count_vowels(s))
print(sorted_words)
['python', 'sort', 'function', 'lambda']

Пример 3. Сортировка координат (кортежей (x,y)) по расстоянию от начала координат:

Пример
points = [(3,4), (1,1), (0,5), (-1,0)]
sorted_points = sorted(points, key=lambda p: p[0]**2 + p[1]**2)
print(sorted_points)
[(-1,0), (1,1), (0,5), (3,4)]

Пример 4. Сортировка списка словарей с преобразованием даты из строки в объект datetime:

Пример
from datetime import datetime
events = [{'date': '2023-05-10', 'name': 'Event A'}, {'date': '2023-01-15', 'name': 'Event B'}, {'date': '2023-12-01', 'name': 'Event C'}]
sorted_events = sorted(events, key=lambda e: datetime.strptime(e['date'], '%Y-%m-%d'))
print(sorted_events)
[{'date': '2023-01-15', 'name': 'Event B'}, {'date': '2023-05-10', 'name': 'Event A'}, {'date': '2023-12-01', 'name': 'Event C'}]

Пример 5. Сортировка с группировкой по первому символу, затем по длине (сложный ключ):

Пример
words = ['apple', 'banana', 'avocado', 'blueberry', 'cherry']
sorted_words = sorted(words, key=lambda w: (w[0], len(w)))
print(sorted_words)
['apple', 'avocado', 'banana', 'blueberry', 'cherry']

Пример 6. Сортировка с условием: сначала отрицательные числа, затем положительные, внутри каждой группы по возрастанию:

Пример
nums = [3, -1, 2, -4, 0, 5]
sorted_nums = sorted(nums, key=lambda x: (x >= 0, abs(x)))
print(sorted_nums)
[-1, -4, 0, 2, 3, 5]

Пример 7. Сортировка списка с None значениями, помещая None в конец:

Пример
values = [5, None, 2, None, 3]
sorted_values = sorted(values, key=lambda x: (x is None, x if x is not None else 0))
print(sorted_values)
[2, 3, 5, None, None]

Пример 8. Сортировка кортежей по убыванию второго элемента, при равенстве по возрастанию первого:

Пример
people = [('Anna', 25), ('Ben', 20), ('Cate', 25)]
sorted_people = sorted(people, key=lambda p: (-p[1], p[0]))
print(sorted_people)
[('Anna', 25), ('Cate', 25), ('Ben', 20)]

Пример 9. Сортировка с lambda внутри list comprehension: отфильтровать четные числа, затем отсортировать по убыванию:

Пример
nums = [10, 2, 5, 8, 1, 7]
even_sorted = sorted([n for n in nums if n % 2 == 0], key=lambda x: -x)
print(even_sorted)
[10, 8, 2]

Пример 10. Сортировка строк, представляющих числа, по их числовому значению:

Пример
str_nums = ['10', '2', '5', '1']
sorted_str_nums = sorted(str_nums, key=lambda s: int(s))
print(sorted_str_nums)
['1', '2', '5', '10']

Сортировка с lambda в Python - comments

En
Python sort lambda (python)