Сортировка данных в Python по произвольному критерию

Раздел: Коллекции -> Сортировка

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

Сортировка по значению в Python

Наиболее эффективный и универсальный способ задать сортировку по произвольному значению - использовать аргумент key. Функция key применяется к каждому элементу и возвращает объект, который будет использоваться для сравнения. Пример со списком кортежей:

data = [(1, 'z'), (2, 'a'), (3, 'm')]
sorted_data = sorted(data, key=lambda x: x[1])
print(sorted_data)

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

[(2, 'a'), (3, 'm'), (1, 'z')]

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

Аргумент reverse=True позволяет изменить порядок на обратный. Такой подход подходит для любых итерабельных объектов, включая списки словарей, строки, пользовательские классы. Важно, что key вызывается однократно для каждого элемента, поэтому вычисления выполняются эффективно.

Типичная ошибка:

попытка передать в key функцию, которая возвращает значения разных типов (например, int и str) - это вызовет TypeError. Решение: привести значения к одному типу в лямбда-функции или предварительно нормализовать данные.

Как отсортировать список кортежей по второму элементу?

pairs = [(5, 'cat'), (1, 'dog'), (3, 'bird')]
sorted_pairs = sorted(pairs, key=lambda item: item[1])
print(sorted_pairs)

Sorted values python (сортировка значений в python (sorted))

[(3, 'bird'), (5, 'cat'), (1, 'dog')]

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

Здесь lambda item: item[1] возвращает второй элемент кортежа, по которому и происходит упорядочивание. Если кортежи имеют разную длину, нужно следить за индексами.

Возможная проблема:

если второй элемент - число, записанное как строка («5» vs 5), то сортировка будет лексикографической. Решение: преобразовать тип в key: key=lambda x: int(x[1]).

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

users = [{'name': 'Alice', 'age': 30}, {'name': 'Bob', 'age': 25}, {'name': 'Charlie', 'age': 35}]
sorted_users = sorted(users, key=lambda u: u['age'])
print(sorted_users)
[{'name': 'Bob', 'age': 25}, {'name': 'Alice', 'age': 30}, {'name': 'Charlie', 'age': 35}]

Более читаемый вариант - использование operator.itemgetter:

from operator import itemgetter
sorted_users = sorted(users, key=itemgetter('age'))
тот же результат

Ошибка:

если ключ отсутствует в словаре, возникнет KeyError. Для безопасной обработки используйте key=lambda u: u.get('age', 0) или itemgetter с default (доступно в Python 3.10+).

Как отсортировать пользовательские объекты по атрибуту?

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

people = [Person('Anna', 28), Person('Ivan', 22), Person('Maria', 30)]
sorted_people = sorted(people, key=lambda p: p.age)
print(sorted_people)
[Person(Ivan, 22), Person(Anna, 28), Person(Maria, 30)]

Для удобства применяется operator.attrgetter:

from operator import attrgetter
sorted_people = sorted(people, key=attrgetter('age'))

Проблема:

если атрибут не определён, возникнет AttributeError. Решение: задать значение по умолчанию через getattr(obj, attr, default) в лямбда-функции.

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

d = {'a': 3, 'b': 1, 'c': 2}
sorted_items = sorted(d.items(), key=lambda item: item[1])
print(sorted_items)
[('b', 1), ('c', 2), ('a', 3)]

Чтобы получить отсортированный словарь (Python 3.7+):

sorted_dict = dict(sorted(d.items(), key=lambda item: item[1]))
print(sorted_dict)
{'b': 1, 'c': 2, 'a': 3}

Ошибка:

сортировка словаря по значениям работает только с уникальными значениями (при равных значениях порядок не определён). Для устойчивой сортировки можно добавить вторичный ключ.

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

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

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

Проблема:

если одно из полей - None, сравнение с другими типами вызовет ошибку. Решение: обернуть поле в кортеж с индикатором, например key=lambda x: (x[0] is None, x[0], x[1]).

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

numbers = [3, 1, 4, 1, 5, 9]
numbers.sort(key=lambda x: -x)  # по убыванию
print(numbers)
[9, 5, 4, 3, 1, 1]

Метод list.sort() изменяет исходный список и возвращает None. Он работает только со списками, в отличие от sorted(), который применим к любой итерации.

Типичная ошибка:

случайно присвоить результат list.sort() переменной (получится None). Всегда используйте sorted() если нужно сохранить исходные данные.

Что делать, если среди значений присутствует None?

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

Кортеж (x is None, x) помещает None в конец (так как True > False). Чтобы поместить None в начало, используйте key=lambda x: (x is not None, x).

Расширенные примеры сортировки по значению

Сортировка строк по длине

Пример
words = ['apple', 'kiwi', 'banana', 'cherry', 'mango']
sorted_by_len = sorted(words, key=len)
print(sorted_by_len)
['kiwi', 'apple', 'mango', 'cherry', 'banana']

В качестве ключа передана встроенная функция len. Аналогично можно использовать str.lower для регистронезависимой сортировки.

Сортировка с игнорированием регистра

Пример
fruits = ['Apple', 'banana', 'Cherry', 'date']
sorted_ignore_case = sorted(fruits, key=str.lower)
print(sorted_ignore_case)
['Apple', 'banana', 'Cherry', 'date']

Функция str.lower возвращает строку в нижнем регистре, по которой и происходит сравнение.

Сортировка словаря по значениям с получением упорядоченного словаря

Пример
scores = {'Math': 90, 'Physics': 85, 'Chemistry': 92, 'Biology': 88}
sorted_scores = dict(sorted(scores.items(), key=lambda item: item[1], reverse=True))
print(sorted_scores)
{'Chemistry': 92, 'Math': 90, 'Biology': 88, 'Physics': 85}

Поскольку в Python 3.7+ словари сохраняют порядок вставки, такой подход удобен для вывода топ-результатов.

Сортировка объектов по вложенному атрибуту

Пример
class Company:
    def __init__(self, name, employees):
        self.name = name
        self.employees = employees

companies = [
    Company('Alpha', [Person('John', 30), Person('Jane', 25)]),
    Company('Beta', [Person('Mike', 35)]),
]
# Сортировка компаний по среднему возрасту сотрудников
def avg_age(company):
    return sum(e.age for e in company.employees) / len(company.employees)

sorted_companies = sorted(companies, key=avg_age)
print([c.name for c in sorted_companies])
['Alpha', 'Beta']

Здесь ключ - пользовательская функция, вычисляющая составное значение.

Использование functools.cmp_to_key для нестандартного порядка

Пример
from functools import cmp_to_key
def compare(x, y):
    # чётные числа идут перед нечётными, затем по возрастанию
    if x % 2 == 0 and y % 2 != 0:
        return -1
    if x % 2 != 0 and y % 2 == 0:
        return 1
    return (x > y) - (x < y)

numbers = [3, 2, 5, 1, 4, 6]
sorted_custom = sorted(numbers, key=cmp_to_key(compare))
print(sorted_custom)
[2, 4, 6, 1, 3, 5]

Старый способ с cmp заменяется на key везде, где возможно, но для сложной логики сравнения cmp_to_key всё ещё применим.

Сортировка по значению с обработкой None и разными типами

Пример
data = [5, 'text', None, 3, 'abc', None]
# Поместить None в конец, строки после чисел (в лексикографическом порядке)
def sort_key(item):
    if item is None:
        return (1, 0, '')
    if isinstance(item, int):
        return (0, 0, item)
    if isinstance(item, str):
        return (0, 1, item)
    return (2, 0, str(item))

sorted_data = sorted(data, key=sort_key)
print(sorted_data)
[3, 5, 'abc', 'text', None, None]

Кортежи позволяют задать произвольные категории и порядок внутри них.

Сортировка по значению в Python - comments

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