Сортировка в Python: понятное руководство по sorted, list.sort, key и многокритериальной сортировке

Сортировка в Python: понятное руководство по sorted, list.sort, key и многокритериальной сортировке

Сортировка в Python: понятное руководство по sorted, list.sort, key и многокритериальной сортировке

Запрос: «сортировка в Python sorted sort key reverse»

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

sorted vs list.sort: в чём разница

  • sorted(iterable, *, key=None, reverse=False) — возвращает новый отсортированный список, не меняя исходные данные.
  • list.sort(*, key=None, reverse=False) — сортирует список на месте и возвращает None.
  • nums = [3, 1, 2]
    print(sorted(nums))     # [1, 2, 3]
    print(nums)             # [3, 1, 2] — исходный список не изменился
    
    nums.sort()
    print(nums)             # [1, 2, 3] — отсортирован на месте
    

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

    Параметр key: сортируем по «ключу»

    Параметр key — это функция, которая для каждого элемента возвращает значение, по которому и будет происходить сравнение. Это быстрее и надёжнее, чем «ручное» сравнение внутри компараторов.

    words = ["Banana", "apple", "cherry"]
    # Регистр-независимая сортировка
    print(sorted(words, key=str.lower))  # ['apple', 'Banana', 'cherry']
    
    people = [
        {"name": "Ann", "age": 30},
        {"name": "Bob", "age": 25},
        {"name": "Carl", "age": 25},
    ]
    # Сортируем список словарей по возрасту
    print(sorted(people, key=lambda p: p["age"]))
    

    reverse=True: сортировка по убыванию

    scores = [10, 70, 30]
    print(sorted(scores, reverse=True))  # [70, 30, 10]
    

    Важно: reverse=True инвертирует порядок уже после вычисления ключей. Это корректный способ сортировать по убыванию. Не меняйте знак ключа для строк, дат и т. п. — это либо не сработает, либо усложнит код.

    Многокритериальная сортировка: кортежи в key

    Python поддерживает сравнение кортежей поэлементно, поэтому удобно возвращать из key несколько полей сразу.

    # Сортировка: возраст по возрастанию, затем имя по алфавиту
    print(sorted(people, key=lambda p: (p["age"], p["name"])) )
    
    # Числа сначала (None в конец), потом по алфавиту
    items = ["b", None, "a", "10"]
    print(sorted(items, key=lambda x: (x is None, str(x))))
    

    Сортировка в Python стабильная: элементы с одинаковым ключом сохраняют исходный порядок. Это позволяет сортировать «по шагам»: сначала по вторичному критерию, затем по первичному — итог будет эквивалентен кортежам в key.

    # Эквивалент многокритериальной сортировке
    people2 = people[:]
    people2.sort(key=lambda p: p["name"])    # 2-й критерий
    people2.sort(key=lambda p: p["age"])     # 1-й критерий (главный)
    

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

    Словари как структура неупорядоченно-упорядоченные по вставке, но сортировать обычно нужно представление: пары items или ключи.

    d = {"c": 3, "a": 1, "b": 2}
    # По ключу
    print(sorted(d.items(), key=lambda kv: kv[0]))  # [('a', 1), ('b', 2), ('c', 3)]
    # По значению
    print(sorted(d.items(), key=lambda kv: kv[1]))  # [('a', 1), ('b', 2), ('c', 3)]
    
    # Список словарей: безопасно с отсутствующими полями
    people3 = [
        {"name": "Ann", "age": 30},
        {"name": "Bob"},                  # age отсутствует
        {"name": "Carl", "age": 25},
    ]
    print(sorted(people3, key=lambda p: (p.get("age") is None, p.get("age", 0))))
    

    Быстрые помощники: operator.itemgetter и attrgetter

    Они чуть быстрее и короче лямбд.

    from operator import itemgetter, attrgetter
    
    # Для словарей
    print(sorted(people, key=itemgetter("age", "name")))
    
    # Для объектов
    class User:
        def __init__(self, name, score):
            self.name = name
            self.score = score
    
    users = [User("Ann", 70), User("Bob", 70), User("Carl", 90)]
    print([u.name for u in sorted(users, key=attrgetter("score", "name"))])
    

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

    По дате

    from datetime import datetime
    
    rows = [
        {"dt": "2025-03-01"},
        {"dt": "2024-11-12"},
        {"dt": "2025-01-10"},
    ]
    print(sorted(rows, key=lambda r: datetime.fromisoformat(r["dt"])))
    

    Если разбор даты дорогой, применяйте «украшать–сортировать–разукрашивать» (DSU): предварительно посчитать ключи и переиспользовать их.

    pairs = [ (datetime.fromisoformat(r["dt"]), r) for r in rows ]
    pairs.sort(key=lambda p: p[0])
    rows_sorted = [ r for _, r in pairs ]
    

    «Естественная» сортировка: file2 перед file10

    import re
    
    def natural_key(s: str):
        return [int(part) if part.isdigit() else part.lower()
                for part in re.split(r"(d+)", s)]
    
    files = ["file2.txt", "file10.txt", "file1.txt"]
    print(sorted(files, key=natural_key))  # ['file1.txt', 'file2.txt', 'file10.txt']
    

    Когда нужен компаратор: functools.cmp_to_key

    Иногда проще описать «как сравнивать два элемента», а не «какой у них ключ». Тогда используйте cmp_to_key для совместимости с key-сортировкой.

    from functools import cmp_to_key
    
    def last_digit_cmp(a, b):
        return (a % 10) - (b % 10)
    
    nums = [21, 32, 14, 45]
    print(sorted(nums, key=cmp_to_key(last_digit_cmp)))  # [21, 32, 14, 45]
    

    Но помните: key-основанная сортировка в Python быстрее и проще. Компараторы используйте только при реальной необходимости.

    Производительность и лучшие практики

  • Сортировки в CPython — Timsort: O(n log n), стабильная, хорошо работает на частично отсортированных данных.
  • Предпочитайте key вместо компараторов — это быстрее и чище.
  • Дорогие ключи кэшируйте (DSU), чтобы не вычислять их многократно.
  • Нужно топ-N? Используйте heapq.nsmallest/nlargest вместо полной сортировки.
  • Смешение типов (числа и строки) в Python 3 сравнивать напрямую нельзя — приводите к общему типу в key.
  • import heapq
    nums = [5, 1, 9, 3, 7]
    print(heapq.nlargest(2, nums))  # [9, 7]
    print(heapq.nsmallest(3, nums)) # [1, 3, 5]
    

    Частые ошибки

  • Ожидание результата от list.sort(): метод возвращает None.
  • Попытка «обратить» сортировку срезом [::-1] вместо reverse=True — это два прохода и не всегда корректно с многокритериальной логикой.
  • Игнорирование None: используйте кортежи в key, чтобы складывать None в конец/начало.
  • Дорогие преобразования в key без кэширования — теряете производительность.
  • Итоги

    Запомните: sorted — создаёт новый список, list.sort — сортирует на месте; key — ваш главный инструмент; сортировка стабильна и поддерживает многокритериальные ключи. Освоив эти приёмы, вы закроете 90% задач по упорядочиванию данных в Python быстро и безопасно.

    Хотите системно прокачать Python с практикой и обратной связью? Попробуйте курс: Пройти «Python с Нуля до Гуру» и уверенно писать чистый, эффективный код.

    Источник

    НЕТ КОММЕНТАРИЕВ

    Оставить комментарий