Как отсортировать список в Python: все способы от простого к продвинутому 🔢
Сортировка — одна из самых частых задач в программировании. Неважно, пишешь ли ты Telegram-бота, парсишь данные или делаешь аналитический отчёт — рано или поздно тебе понадобится упорядочить элементы списка. И тут новички начинают гуглить, путать sort() и sorted(), ломают голову над лямбда-функциями и наступают на одни и те же грабли.
Эта статья — полный разбор того, как отсортировать список в Питоне. От базовой сортировки чисел и строк до сложных случаев с вложенными структурами, пользовательскими ключами и обратным порядком. Без воды, с примерами кода, которые можно скопировать и сразу запустить.
Если ты только учишь Python или уже пишешь проекты, но каждый раз забываешь, какой метод сортировки использовать — сохрани эту статью в закладки. Здесь разобрано всё, что нужно.
Базовая сортировка списка: sort() и sorted() — в чём разница 🔄
В Python есть два способа отсортировать список, и новички постоянно их путают. Разница принципиальная: один меняет исходный список, а второй создаёт новый. Запомни это — и половина ошибок уйдёт сама.
Метод sort() — сортирует список на месте. Он изменяет тот список, к которому применяется, и ничего не возвращает (точнее, возвращает None). Используй его, когда исходный порядок не нужен и ты хочешь сэкономить память.
Функция sorted() — создаёт новый отсортированный список. Оригинальный список остаётся нетронутым. Применяй, когда нужно сохранить первоначальные данные.
# Условие: есть список чисел, нужно отсортировать двумя способами # Способ 1: sort() — меняет исходный список numbers = [5, 2, 8, 1, 9, 3] numbers.sort() print(numbers) # [1, 2, 3, 5, 8, 9] # Способ 2: sorted() — создаёт новый список numbers = [5, 2, 8, 1, 9, 3] result = sorted(numbers) print(result) # [1, 2, 3, 5, 8, 9] print(numbers) # [5, 2, 8, 1, 9, 3] — оригинал не изменился!
Разбор по строкам:
numbers.sort()— вызывает методsort()у спискаnumbers. Список сортируется на месте, метод возвращаетNone.sorted(numbers)— передаёт список в функциюsorted(), которая возвращает новый список. Оригиналnumbersне трогается.print(numbers)во втором случае показывает, что порядок остался прежним — это и есть ключевое отличие.
Частая ошибка новичка:
# ОШИБКА: пытаются сохранить результат sort() numbers = [5, 2, 8, 1] sorted_numbers = numbers.sort() print(sorted_numbers) # None!
sort() возвращает None, поэтому в sorted_numbers лежит None, а не отсортированный список. Если нужно сохранить результат в переменную — используй sorted().
Совет: запомни простое правило. Нужно изменить оригинальный список — sort(). Нужно получить отсортированную копию — sorted(). Третьего не дано.
Сортировка в обратном порядке и по разным типам данных ⬇️
По умолчанию sort() и sorted() сортируют по возрастанию. Но часто нужно обратное — от большего к меньшему. И тут оба способа поддерживают параметр reverse.
Обратная сортировка
# Условие: отсортировать список оценок от высшей к низшей grades = [3, 5, 4, 2, 5, 4, 3] grades.sort(reverse=True) print(grades) # [5, 5, 4, 4, 3, 3, 2]
Разбор:
reverse=True— параметр, который меняет направление сортировки на убывание.reverse=False— значение по умолчанию, сортировка по возрастанию.
Сортировка строк
Строки в Python сортируются лексикографически — по кодам символов, как в словаре. Но есть нюанс с регистром.
# Сортировка строк по алфавиту fruits = ["банан", "яблоко", "вишня", "апельсин"] print(sorted(fruits)) # ['апельсин', 'банан', 'вишня', 'яблоко'] # Сортировка строк разного регистра words = ["банан", "Яблоко", "вишня", "Апельсин"] print(sorted(words)) # ['Апельсин', 'Яблоко', 'банан', 'вишня'] # Прописные буквы идут раньше строчных!
Почему «Апельсин» и «Яблоко» оказались в начале? Заглавные буквы имеют меньшие коды в Unicode, чем строчные. Чтобы игнорировать регистр, используй параметр key:
words = ["банан", "Яблоко", "вишня", "Апельсин"] print(sorted(words, key=str.lower)) # ['Апельсин', 'банан', 'вишня', 'Яблоко']
Разбор:
key=str.lower— передаёт функциюstr.lowerкак ключ сортировки. Каждое слово приводится к нижнему регистру перед сравнением, но сам результат остаётся с оригинальным регистром.
Сортировка чисел как строк
Ещё один подвох: если числа хранятся в виде строк, сортировка будет лексикографической, а не числовой.
numbers_str = ["10", "2", "1", "20", "100"] print(sorted(numbers_str)) # ['1', '10', '100', '2', '20'] — не числовой порядок! print(sorted(numbers_str, key=int)) # ['1', '2', '10', '20', '100'] — теперь правильно
key=int преобразует каждую строку в число перед сравнением. Сами элементы остаются строками, но порядок — числовой.
Совет: всегда проверяй тип данных перед сортировкой. Строки сортируются как текст, числа — как числа. Смешанные типы в одном списке вызовут TypeError.
Параметр key: сортировка по кастомному правилу 🎯
Параметр key — самый мощный инструмент сортировки в Python. Он принимает функцию, которая применяется к каждому элементу, и результат функции используется для сравнения. Сам элемент при этом не меняется.
Сортировка по длине строки
# Условие: отсортировать слова по длине, от коротких к длинным words = ["информатика", "код", "программа", "Питон", "ИИ"] result = sorted(words, key=len) print(result) # ['ИИ', 'код', 'Питон', 'программа', 'информатика']
Разбор:
key=len— встроенная функцияlen()применяется к каждому элементу. Слова сравниваются по длине, а не по алфавиту.
Сортировка словарей в списке
Это классическая задача: у тебя есть список словарей, и нужно отсортировать по значению одного из ключей.
# Условие: список студентов с оценками, отсортировать по убыванию балла
students = [
{"name": "Анна", "score": 88},
{"name": "Борис", "score": 95},
{"name": "Вера", "score": 72},
{"name": "Глеб", "score": 95},
]
# Сортировка по баллу (по убыванию)
result = sorted(students, key=lambda x: x["score"], reverse=True)
for s in result:
print(f"{s['name']}: {s['score']}")
# Борис: 95
# Глеб: 95
# Анна: 88
# Вера: 72
Разбор по строкам:
lambda x: x["score"]— анонимная функция, которая берёт словарьxи возвращает значение по ключу"score". Именно это значение используется для сравнения.reverse=True— сортировка по убыванию, от максимума к минимуму.- Борис и Глеб оба набрали 95. Python сохраняет их исходный порядок — это называется стабильная сортировка. Элементы с равными ключами не меняют позиций относительно друг друга.
Сортировка по нескольким критериям
Что если два студента набрали одинаковый балл и нужно отсортировать их по имени? Для этого передай key в виде кортежа.
# Условие: сортируем по баллу (убывание), при равенстве — по имени (возрастание)
students = [
{"name": "Борис", "score": 95},
{"name": "Анна", "score": 88},
{"name": "Глеб", "score": 95},
{"name": "Дарья", "score": 88},
]
result = sorted(students, key=lambda x: (-x["score"], x["name"]))
for s in result:
print(f"{s['name']}: {s['score']}")
# Борис: 95
# Глеб: 95
# Анна: 88
# Дарья: 88
Разбор:
(-x["score"], x["name"])— кортеж из двух элементов. Минус перед"score"даёт сортировку по убыванию для чисел (больше число → меньше значение с минусом → раньше в списке). Имя сортируется по возрастанию.- Для строк минус не работает, поэтому если нужно сортировать строку по убыванию, используй отдельный
sortedсreverseили модульoperator.
Модуль operator — замена лямбдам
Лямбды удобны, но для частых случаев есть готовые функции в модуле operator:
from operator import itemgetter, attrgetter
# itemgetter — для словарей и кортежей
students = [("Анна", 88), ("Борис", 95), ("Вера", 72)]
result = sorted(students, key=itemgetter(1), reverse=True)
print(result) # [('Борис', 95), ('Анна', 88), ('Вера', 72)]
# Сортировка по двум полям
result = sorted(students, key=itemgetter(1, 0))
print(result) # [('Вера', 72), ('Анна', 88), ('Борис', 95)]
itemgetter(1) возвращает функцию, которая извлекает элемент с индексом 1. Аналогично itemgetter(1, 0) извлекает кортеж (элемент_1, элемент_0) — сортировка по двум полям сразу.
Совет: если key-функция становится длинной и запутанной, вынеси её в отдельную def-функцию. Лямбды хороши для одной строки, не больше.
Сложные случаи: вложенные списки, кортежи и объекты 🧩
В реальных проектах данные редко лежат в простом списке чисел. Чаще ты работаешь со списками кортежей, вложенными структурами или объектами классов. Принцип тот же — определяешь key и сортируешь.
Сортировка списка кортежей
# Условие: список координат точек, отсортировать по удалённости от нуля points = [(3, 4), (1, 1), (0, 2), (5, 12), (8, 6)] result = sorted(points, key=lambda p: (p[0]**2 + p[1]**2) ** 0.5) print(result) # [(1, 1), (0, 2), (3, 4), (8, 6), (5, 12)]
Разбор:
lambda p: (p[0]**2 + p[1]**2) ** 0.5— вычисляет расстояние от точки до начала координат по теореме Пифагора. Сортировка идёт по этому расстоянию, но сами точки остаются нетронутыми.
Сортировка объектов классов
Если у тебя есть список объектов и нужно отсортировать по атрибуту, используем attrgetter:
from operator import attrgetter
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __repr__(self):
return f"{self.name}({self.score})"
students = [
Student("Анна", 88),
Student("Борис", 95),
Student("Вера", 72),
]
result = sorted(students, key=attrgetter("score"), reverse=True)
print(result) # [Борис(95), Анна(88), Вера(72)]
Разбор:
attrgetter("score")— извлекает атрибутscoreиз каждого объекта. Аналогlambda x: x.score, но компактнее.__repr__нужен, чтобы приprintобъект выводился читаемо, а не как<__main__.Student object at 0x...>.
Сортировка с cmp_to_key — старый стиль
В Python 2 был параметр cmp, который принимал функцию-компаратор (возвращала -1, 0 или 1). В Python 3 его убрали, но если нужно использовать старую логику, есть мост cmp_to_key из модуля functools:
from functools import cmp_to_key
# Условие: сортировка чисел по количеству делителей
def count_divisors(n):
count = 0
for i in range(1, n + 1):
if n % i == 0:
count += 1
return count
numbers = [15, 6, 10, 12, 7]
# Через key — проще и быстрее
result = sorted(numbers, key=count_divisors)
print(result) # [7, 6, 10, 15, 12]
# Через cmp_to_key — если нужна сложная логика сравнения
def compare(a, b):
da, db = count_divisors(a), count_divisors(b)
if da < db:
return -1
elif da > db:
return 1
else:
return 0
result = sorted(numbers, key=cmp_to_key(compare))
print(result) # [7, 6, 10, 15, 12]
В 95% случаев key справляется лучше и быстрее, чем cmp_to_key. Используй компаратор, только если логика сравнения не сводится к вычислению одного значения.
Совет: для сложных структур данных сначала попробуй выразить критерий сортировки через key-функцию. Если не получается — тогда cmp_to_key. Но чаще всего первый вариант работает.
Подводные камни и типичные ошибки при сортировке ⚠️
Разберём ошибки, на которые спотыкаются новички. Некоторые из них приводят к неправильному результату без сообщения об ошибке — и это самое коварное.
1. Смешанные типы в списке
data = [5, "3", 2, "10", 1] print(sorted(data)) # TypeError: '<' not supported between 'int' and 'str'
Python не может сравнить число и строку. Решение — привести всё к одному типу:
data = [5, "3", 2, "10", 1] print(sorted(data, key=int)) # [1, 2, 5, '10', '3']
Здесь key=int работает: строки "3" и "10" преобразуются в числа для сравнения. Но результат содержит смешанные типы — оригинальные элементы не меняются.
2. Изменение списка во время итерации
# ПЛОХО: меняешь список, по которому итерируешься
numbers = [3, 1, 4, 1, 5]
for i in range(len(numbers)):
numbers.sort()
print(numbers[i])
# Результат непредсказуем, потому что сортировка сдвигает элементы
Решение: сортируй до цикла, а не внутри:
numbers = [3, 1, 4, 1, 5]
numbers.sort()
for n in numbers:
print(n) # 1, 1, 3, 4, 5
3. Ожидание, что sort() вернёт список
Мы уже разбирали это, но повторю, потому что ошибка встречается постоянно:
result = [3, 1, 4].sort() print(result) # None
sort() всегда возвращает None. Нужно — используй sorted().
4. Сортировка множества или словаря напрямую
my_set = {5, 2, 8, 1}
my_set.sort() # AttributeError: 'set' object has no attribute 'sort'
У множеств нет порядка, поэтому метода sort() у них нет. Но sorted() работает с любым итерируемым объектом:
my_set = {5, 2, 8, 1}
result = sorted(my_set)
print(result) # [1, 2, 5, 8]
Обрати внимание: результат — список, а не множество. sorted() всегда возвращает список, независимо от типа входных данных.
5. Неправильная сортировка с None в данных
data = [5, None, 2, None, 8] print(sorted(data)) # TypeError: '<' not supported between 'NoneType' and 'int'
Решение: обработай None в key-функции:
data = [5, None, 2, None, 8] result = sorted(data, key=lambda x: (x is None, x)) print(result) # [2, 5, 8, None, None]
Разбор: x is None возвращает True (1) или False (0) для каждого элемента. None-значения оказываются в конце, потому что True > False. Если нужно None в начале — поменяй местами: key=lambda x: (x is not None, x).
Совет: если в данных могут быть None, всегда обрабатывай их в key-функции. Python не умеет сравнивать None с числами и строками — получишь TypeError.
Производительность сортировки и алгоритм Timsort 🚀
Python использует алгоритм Timsort — гибрид сортировки слиянием и сортировки вставками. Он разработан Тимом Питерсом в 2002 году специально для Python и оптимизирован для реальных данных, которые часто содержат уже отсортированные фрагменты.
Характеристики Timsort:
- Время в худшем случае: $O(n \log n)$
- В лучшем случае (данные уже отсортированы): $O(n)$
- Стабильность: сортировка стабильна — элементы с равными ключами сохраняют исходный порядок
- Доп. память: $O(n)$
Это значит, что встроенная сортировка Python достаточно быстрая для большинства задач. Но есть нюансы:
Сложность key-функции влияет на скорость. Если key-функция вызывает дорогие вычисления, Python применяет её к каждому элементу на каждом сравнении. Точнее, Python вызывает её один раз на элемент и кэширует результат — это называется декорирование-сортировка-декорирование (decorate-sort-undecorate). Но всё равно, чем проще key, тем быстрее работает сортировка.
import time
data = list(range(100000, 0, -1))
# Быстрый key
start = time.time()
result = sorted(data)
print(f"Без key: {time.time() - start:.4f} сек")
# Медленный key — вычисление для каждого элемента
start = time.time()
result = sorted(data, key=lambda x: sum(int(d) for d in str(x)))
print(f"Сложный key: {time.time() - start:.4f} сек")
На 100 000 элементов разница может быть в десятки раз. Простой key=len или key=int почти не замедляет сортировку, а вычисление факториала для каждого элемента — сильно.
Когда стоит задуматься о производительности:
- Список больше 100 000 элементов
key-функция делает сложные вычисления- Сортировка вызывается в цикле много раз
В этих случаях имеет смысл предварительно вычислить ключи и сохранить их, или использовать numpy для числовых данных.
Совет: не оптимизируй сортировку заранее. Встроенные sort() и sorted() работают достаточно быстро для 99% задач. Оптимизация нужна только там, где профайлер показал реальное узкое место.
10 часто задаваемых вопросов
- Чем отличаются
sort()иsorted()в Python?sort()сортирует список на месте и возвращаетNone.sorted()создаёт новый отсортированный список и не меняет оригинал.
- Как отсортировать список по убыванию? Передай параметр
reverse=True:sorted(data, reverse=True)илиdata.sort(reverse=True).
- Как отсортировать список словарей по значению ключа? Используй
keyс лямбдой:sorted(data, key=lambda x: x["ключ"]).
- Как отсортировать список строк без учёта регистра? Передай
key=str.lowerвsort()илиsorted().
- Можно ли отсортировать множество (set) в Python? Множества не имеют порядка, поэтому
sort()у них нет. Ноsorted()работает и вернёт отсортированный список.
- Как отсортировать список кортежей по второму элементу?
sorted(data, key=lambda x: x[1])илиsorted(data, key=itemgetter(1)).
- Что такое стабильная сортировка? Это сортировка, при которой элементы с равными ключами сохраняют свой изначальный порядок. Сортировка в Python — стабильная.
- Как отсортировать список, в котором есть
None? ОбработайNoneвkey-функции:key=lambda x: (x is None, x)— поставитNoneв конец.
- Какой алгоритм сортировки использует Python? Timsort — гибрид сортировки слиянием и вставками. Сложность $O(n \log n)$, в лучшем случае $O(n)$.
- Как отсортировать список сразу по двум критериям? Верни кортеж из
key-функции:key=lambda x: (x["score"], x["name"])— сортировка по баллу, при равенстве по имени.
Сортировка списков в Python — тема, которая кажется простой, пока не столкнёшься с реальными данными. Но если ты разобрался с key-функциями, лямбдами и знаешь разницу между sort() и sorted(), то 90% задач закроешь без гугления.
Хочешь узнать, какой курс обучения Python подходит именно тебе? Пройди опрос:
Сайт: https://infazy.ru/podobrat-kurs-python
Телеграмм бот: https://t.me/kursypythonbot
#Python #СортировкаСпискаPython #СортировкаПитон #PythonДляНовичков #СпискиPython #ФункцияSorted #МетодSort #КлючСортировкиPython #ЛямбдаПитон #ПрограммированиеНаПитоне #PythonУроки #ОбучениеПрограммированию
