Python сортировка списков словарей: 5 способов упорядочить данные
Для кого эта статья:
- Python-разработчики
- Студенты и профессионалы, стремящиеся улучшить навыки работы с данными
Люди, заинтересованные в оптимизации и улучшении производительности кода
Работа с данными в Python неизбежно приводит к необходимости сортировки сложных структур. Особенно часто программисты сталкиваются с задачей упорядочивания списков словарей — будь то данные пользователей, товаров или любой другой коллекции объектов. Эта задача кажется простой только на первый взгляд. Когда в проекте появляются требования к сортировке по нескольким параметрам, многие разработчики теряются в вариантах решения. Я подготовил пять проверенных способов, которые превратят хаотичную коллекцию словарей в идеально упорядоченный массив данных. 💻
Сортировка коллекций данных — одна из фундаментальных операций, которой должен владеть каждый Python-разработчик. Если вы хотите освоить не только базовые, но и продвинутые техники работы с данными, а затем применить их в реальных веб-проектах, обратите внимание на курс Обучение Python-разработке от Skypro. На курсе вы не только научитесь виртуозно обрабатывать данные, но и создадите полноценные веб-приложения с использованием фреймворков Flask и Django. Структурированный подход к обучению поможет быстро перейти от теории к практике.
Основы сортировки списков словарей в Python
Работа со списками словарей — это почти повседневная задача для Python-разработчика. Сортировка таких структур имеет свои особенности, связанные с тем, что нам необходимо определить, по какому именно ключу в словаре будет выполняться упорядочивание.
Рассмотрим базовый пример: у нас есть список словарей с информацией о сотрудниках компании:
employees = [
{'name': 'Алексей', 'salary': 120000, 'experience': 5},
{'name': 'Мария', 'salary': 150000, 'experience': 3},
{'name': 'Иван', 'salary': 90000, 'experience': 2},
{'name': 'Елена', 'salary': 135000, 'experience': 7}
]
Допустим, нам нужно отсортировать этот список по зарплате (ключ 'salary'). Встроенная функция sorted() в Python принимает необязательный параметр key, который определяет, как элементы будут сравниваться между собой.
Базовый синтаксис для сортировки списка словарей выглядит так:
sorted_list = sorted(list_of_dicts, key=function_to_extract_sort_key)
Где function_to_extract_sort_key — это функция, которая принимает элемент списка (в нашем случае словарь) и возвращает значение, по которому будет производиться сортировка.
Максим Петров, ведущий разработчик Python
Недавно я работал над проектом для крупного интернет-магазина, где требовалось визуализировать данные о продажах. Клиент жаловался, что отчеты выглядят хаотично и сложны для анализа. После изучения кода я обнаружил, что проблема крылась в неправильной сортировке данных.
Данные о продуктах хранились в виде списка словарей:
products = [ {'id': 5, 'name': 'Смартфон', 'sales': 1200, 'rating': 4.5}, {'id': 3, 'name': 'Наушники', 'sales': 800, 'rating': 4.2}, {'id': 7, 'name': 'Ноутбук', 'sales': 350, 'rating': 4.8} ]Предыдущий разработчик использовал крайне неэффективный подход с созданием промежуточных списков для сортировки. Я заменил всего пару строк кода, применив
sorted()с lambda-функцией:sorted_products = sorted(products, key=lambda x: x['sales'], reverse=True)Эта простая замена не только сделала код чище, но и значительно ускорила генерацию отчетов. Клиент был в восторге от того, как маленькое изменение в коде полностью преобразило его аналитическую панель.
Существует несколько основных подходов к сортировке списков словарей в Python:
- Использование lambda-функций с
sorted() - Применение функции
itemgetterиз модуляoperator - Метод
sort()для изменения списка на месте - Создание отдельной функции-ключа для сортировки
- Комбинированные подходы для сортировки по нескольким критериям
Каждый из этих подходов имеет свои преимущества и подходит для решения определенных задач. 🔍
| Способ сортировки | Производительность | Краткость кода | Читаемость |
|---|---|---|---|
sorted() с lambda | Средняя | Высокая | Средняя |
operator.itemgetter | Высокая | Средняя | Высокая |
list.sort() | Высокая | Высокая | Средняя |
| Функция-ключ | Средняя | Низкая | Высокая |
| Многоключевая сортировка | Средняя | Низкая | Средняя |

Сортировка с помощью функции sorted() и lambda-выражений
Самый распространенный подход к сортировке списка словарей использует встроенную функцию sorted() в сочетании с lambda-выражениями. Это мощный и гибкий способ, позволяющий быстро указать ключ для сортировки.
Вот как мы можем отсортировать наш список сотрудников по зарплате:
# Сортировка по возрастанию зарплаты
sorted_by_salary = sorted(employees, key=lambda employee: employee['salary'])
# Результат:
# [{'name': 'Иван', 'salary': 90000, 'experience': 2},
# {'name': 'Алексей', 'salary': 120000, 'experience': 5},
# {'name': 'Елена', 'salary': 135000, 'experience': 7},
# {'name': 'Мария', 'salary': 150000, 'experience': 3}]
Здесь lambda employee: employee['salary'] — это анонимная функция, которая принимает словарь (элемент списка) и возвращает значение ключа 'salary'. Функция sorted() использует эти значения для сравнения элементов и построения отсортированного списка.
Если нам нужна сортировка по убыванию, можно воспользоваться параметром reverse:
# Сортировка по убыванию зарплаты
sorted_by_salary_desc = sorted(employees, key=lambda employee: employee['salary'], reverse=True)
# Результат:
# [{'name': 'Мария', 'salary': 150000, 'experience': 3},
# {'name': 'Елена', 'salary': 135000, 'experience': 7},
# {'name': 'Алексей', 'salary': 120000, 'experience': 5},
# {'name': 'Иван', 'salary': 90000, 'experience': 2}]
Lambda-выражения особенно удобны, когда требуется выполнить более сложную логику при извлечении ключа для сортировки:
# Сортировка по соотношению зарплаты к опыту работы (ценность сотрудника)
sorted_by_value = sorted(employees, key=lambda employee: employee['salary'] / employee['experience'])
# Результат:
# [{'name': 'Иван', 'salary': 90000, 'experience': 2},
# {'name': 'Елена', 'salary': 135000, 'experience': 7},
# {'name': 'Алексей', 'salary': 120000, 'experience': 5},
# {'name': 'Мария', 'salary': 150000, 'experience': 3}]
Преимущества использования sorted() с lambda-выражениями:
- Лаконичность — весь код умещается в одну строку
- Гибкость — можно легко изменить логику извлечения ключа
- Не изменяет исходный список — создается новый отсортированный список
- Удобно для быстрой разовой сортировки
Однако у этого подхода есть и недостатки:
- Lambda-функции немного менее эффективны, чем специализированные функции из модуля
operator - При частом использовании одной и той же логики сортировки лучше вынести её в отдельную именованную функцию
- Для очень сложной логики сортировки lambda-выражения могут стать нечитаемыми
В большинстве случаев этот метод будет оптимальным выбором благодаря своей простоте и гибкости. 🚀
Использование operator.itemgetter для оптимизации сортировки
Если вам часто приходится сортировать списки словарей и вы заботитесь о производительности, стоит обратить внимание на функцию itemgetter из модуля operator. Этот способ не только более эффективен с точки зрения производительности, но и делает код более чистым и понятным.
Функция itemgetter создает объект-извлекатель, который может быть использован для получения определенного элемента или ключа из итерируемых объектов или отображений.
from operator import itemgetter
# Сортировка по возрастанию зарплаты
sorted_by_salary = sorted(employees, key=itemgetter('salary'))
# Результат такой же, как и при использовании lambda
Использование itemgetter особенно полезно, когда вы сортируете большие наборы данных или выполняете сортировку многократно в своём коде. Функции, созданные с помощью itemgetter, выполняются быстрее, чем эквивалентные lambda-выражения, поскольку они оптимизированы именно для этой цели.
Вот пример сортировки по убыванию с использованием itemgetter:
# Сортировка по убыванию зарплаты
sorted_by_salary_desc = sorted(employees, key=itemgetter('salary'), reverse=True)
Одно из главных преимуществ itemgetter — возможность сортировки по нескольким ключам за один проход:
# Сортировка сначала по опыту, затем по зарплате
sorted_by_experience_salary = sorted(employees, key=itemgetter('experience', 'salary'))
# Результат:
# [{'name': 'Иван', 'salary': 90000, 'experience': 2},
# {'name': 'Мария', 'salary': 150000, 'experience': 3},
# {'name': 'Алексей', 'salary': 120000, 'experience': 5},
# {'name': 'Елена', 'salary': 135000, 'experience': 7}]
В этом примере сначала выполняется сортировка по ключу 'experience', а для записей с одинаковым опытом работы применяется сортировка по ключу 'salary'.
Анна Соколова, DevOps-инженер
В нашей компании возникла проблема производительности при обработке логов сервера. Система мониторинга собирала миллионы записей в виде списка словарей:
PythonСкопировать кодlogs = [ {'timestamp': '2023-05-12T15:45:32', 'service': 'auth', 'level': 'ERROR', 'message': '...'}, {'timestamp': '2023-05-12T15:45:28', 'service': 'api', 'level': 'INFO', 'message': '...'}, # тысячи записей... ]Для анализа эти логи нужно было постоянно сортировать по разным критериям. Изначально мы использовали lambda-функции, но сортировка занимала неприемлемо долгое время при работе с большими объемами данных:
PythonСкопировать код# Старый код sorted_logs = sorted(logs, key=lambda x: (x['level'], x['timestamp']))Когда я заменила lambda на itemgetter, время обработки сократилось почти на 40%:
PythonСкопировать код# Новый код from operator import itemgetter sorted_logs = sorted(logs, key=itemgetter('level', 'timestamp'))Это изменение позволило нам обрабатывать логи в реальном времени и быстрее реагировать на критические ошибки. Теперь мы используем
itemgetterво всех скриптах, где требуется сортировка словарей, особенно при работе с большими объемами данных.
| Критерий сравнения | Lambda-выражения | operator.itemgetter |
|---|---|---|
| Синтаксическая простота | Средняя | Высокая |
| Скорость на малых объемах | Сравнимая | Сравнимая |
| Скорость на больших объемах | Ниже | Выше (на ~20-40%) |
| Сортировка по нескольким ключам | Требует сложной записи | Простая запись |
| Поддержка сложной логики | Да | Ограничена |
| Необходимость импорта | Нет | Да |
Преимущества использования operator.itemgetter:
- Повышенная производительность по сравнению с lambda, особенно на больших наборах данных
- Более чистый и читаемый код при сортировке по нескольким ключам
- Меньше возможностей для ошибок в логике сортировки
Недостатки:
- Требует импорта модуля
operator - Ограниченная гибкость по сравнению с lambda (нельзя выполнять произвольные вычисления)
- Не подходит, если ключ для сортировки нужно вычислять по сложной формуле
Для большинства стандартных задач сортировки itemgetter — оптимальный выбор с точки зрения баланса между читаемостью и производительностью. 🔧
Метод sort() для изменения списка на месте
В отличие от функции sorted(), которая возвращает новый отсортированный список, метод sort() модифицирует исходный список. Это может быть предпочтительнее, когда вы не нуждаетесь в сохранении оригинального порядка и хотите сэкономить память.
Давайте посмотрим, как применить метод sort() к нашему списку сотрудников:
# Создаем копию списка, чтобы не менять оригинал для следующих примеров
employees_copy = employees.copy()
# Сортировка по возрастанию зарплаты
employees_copy.sort(key=lambda employee: employee['salary'])
# Теперь employees_copy содержит отсортированные данные
print(employees_copy)
# Результат:
# [{'name': 'Иван', 'salary': 90000, 'experience': 2},
# {'name': 'Алексей', 'salary': 120000, 'experience': 5},
# {'name': 'Елена', 'salary': 135000, 'experience': 7},
# {'name': 'Мария', 'salary': 150000, 'experience': 3}]
Метод sort() также принимает параметры key и reverse, работающие точно так же, как и в функции sorted():
# Создаем еще одну копию списка
employees_copy2 = employees.copy()
# Сортировка по убыванию опыта работы
employees_copy2.sort(key=lambda employee: employee['experience'], reverse=True)
# Результат:
# [{'name': 'Елена', 'salary': 135000, 'experience': 7},
# {'name': 'Алексей', 'salary': 120000, 'experience': 5},
# {'name': 'Мария', 'salary': 150000, 'experience': 3},
# {'name': 'Иван', 'salary': 90000, 'experience': 2}]
Метод sort() также совместим с operator.itemgetter, что делает его более эффективным при работе с большими наборами данных:
from operator import itemgetter
# Создаем еще одну копию списка
employees_copy3 = employees.copy()
# Сортировка по имени
employees_copy3.sort(key=itemgetter('name'))
# Результат:
# [{'name': 'Алексей', 'salary': 120000, 'experience': 5},
# {'name': 'Елена', 'salary': 135000, 'experience': 7},
# {'name': 'Иван', 'salary': 90000, 'experience': 2},
# {'name': 'Мария', 'salary': 150000, 'experience': 3}]
Когда стоит использовать метод sort() вместо функции sorted()?
- Когда вам не нужно сохранять исходный порядок элементов
- При работе с очень большими списками, где создание новой копии может быть затратно по памяти
- Когда логика программы подразумевает последовательную модификацию списка
Главное помнить, что sort() изменяет список непосредственно и возвращает None, поэтому попытка присвоить его результат переменной приведет к ошибке:
# Неправильно! Это присвоит None переменной sorted_employees
sorted_employees = employees.sort(key=lambda employee: employee['salary'])
# Правильно
employees.sort(key=lambda employee: employee['salary'])
sorted_employees = employees # Теперь они ссылаются на один и тот же отсортированный список
Преимущества метода sort():
- Экономия памяти, поскольку не создаётся новый список
- Более эффективен с точки зрения производительности
- Идеален для однократной сортировки без необходимости сохранения оригинального порядка
Недостатки:
- Изменяет исходные данные, что может быть нежелательно
- Возвращает
None, что может привести к ошибкам при неправильном использовании - Не подходит, если нужно сохранить оригинальный порядок элементов
Выбор между sort() и sorted() зависит от контекста задачи и требований к обработке данных. Метод sort() особенно полезен в сценариях, где минимизация использования памяти является приоритетом. 🧩
Продвинутые техники: сортировка по нескольким ключам
В реальных проектах часто требуется более сложная сортировка — например, упорядочивание элементов по нескольким критериям с различными приоритетами. Python предоставляет элегантные способы для решения таких задач.
Рассмотрим задачу сортировки списка сотрудников сначала по отделу, затем по зарплате в убывающем порядке, а при равных зарплатах — по опыту:
# Расширим наш список сотрудников
employees_extended = [
{'name': 'Алексей', 'salary': 120000, 'experience': 5, 'department': 'IT'},
{'name': 'Мария', 'salary': 150000, 'experience': 3, 'department': 'Marketing'},
{'name': 'Иван', 'salary': 90000, 'experience': 2, 'department': 'IT'},
{'name': 'Елена', 'salary': 135000, 'experience': 7, 'department': 'Marketing'},
{'name': 'Дмитрий', 'salary': 120000, 'experience': 4, 'department': 'IT'},
{'name': 'Ольга', 'salary': 135000, 'experience': 5, 'department': 'HR'},
{'name': 'Сергей', 'salary': 90000, 'experience': 6, 'department': 'HR'}
]
Используя lambda-функции, мы можем создать кортеж из значений ключей для многоуровневой сортировки:
# Сортировка: сначала по отделу, затем по убыванию зарплаты, затем по опыту
sorted_complex = sorted(
employees_extended,
key=lambda employee: (
employee['department'], # Первичный критерий
-employee['salary'], # Вторичный критерий (минус для сортировки по убыванию)
employee['experience'] # Третичный критерий
)
)
# Результат:
# [{'name': 'Ольга', 'salary': 135000, 'experience': 5, 'department': 'HR'},
# {'name': 'Сергей', 'salary': 90000, 'experience': 6, 'department': 'HR'},
# {'name': 'Алексей', 'salary': 120000, 'experience': 5, 'department': 'IT'},
# {'name': 'Дмитрий', 'salary': 120000, 'experience': 4, 'department': 'IT'},
# {'name': 'Иван', 'salary': 90000, 'experience': 2, 'department': 'IT'},
# {'name': 'Елена', 'salary': 135000, 'experience': 7, 'department': 'Marketing'},
# {'name': 'Мария', 'salary': 150000, 'experience': 3, 'department': 'Marketing'}]
Обратите внимание на использование минуса перед employee['salary']. Это простой трюк для сортировки числовых значений в обратном порядке. Однако этот подход работает только с числовыми данными.
Для более сложных случаев, когда не все ключи могут быть обработаны одинаково (например, некоторые в порядке возрастания, а другие убывания), можно использовать attrgetter и itemgetter из модуля operator в сочетании с классом functools.cmp_to_key:
from functools import cmp_to_key
def complex_comparison(a, b):
# Сравнение по отделу (в алфавитном порядке)
if a['department'] != b['department']:
return -1 if a['department'] < b['department'] else 1
# Если отделы одинаковые, сравниваем по зарплате (по убыванию)
if a['salary'] != b['salary']:
return -1 if a['salary'] > b['salary'] else 1
# Если зарплаты одинаковые, сравниваем по опыту (по возрастанию)
return -1 if a['experience'] < b['experience'] else 1
# Применяем нашу функцию сравнения
sorted_with_cmp = sorted(employees_extended, key=cmp_to_key(complex_comparison))
Если в вашем случае нужно сортировать по нескольким ключам, но все в одном направлении (по возрастанию или убыванию), то operator.itemgetter будет более эффективным решением:
from operator import itemgetter
# Сортировка по отделу, затем по зарплате, затем по опыту (все по возрастанию)
sorted_with_itemgetter = sorted(employees_extended, key=itemgetter('department', 'salary', 'experience'))
# Для обратного порядка всего списка можно использовать reverse=True
sorted_reverse = sorted(
employees_extended,
key=itemgetter('department', 'salary', 'experience'),
reverse=True
)
Однако itemgetter не позволяет указывать разные направления сортировки для разных ключей. Для этого потребуется использовать lambda или создать специальную функцию.
Еще один интересный подход — создание класса, который определяет свое поведение при сравнении:
class SortableEmployee:
def __init__(self, employee_dict):
self.data = employee_dict
def __lt__(self, other):
# Сначала сравниваем по отделу
if self.data['department'] != other.data['department']:
return self.data['department'] < other.data['department']
# При равных отделах, сравниваем по зарплате (в обратном порядке)
if self.data['salary'] != other.data['salary']:
return self.data['salary'] > other.data['salary']
# При равных зарплатах, сравниваем по опыту
return self.data['experience'] < other.data['experience']
# Оборачиваем каждый словарь в наш класс
sortable_employees = [SortableEmployee(emp) for emp in employees_extended]
# Сортируем
sortable_employees.sort()
# Преобразуем обратно в список словарей
sorted_result = [emp.data for emp in sortable_employees]
Этот подход может показаться избыточным для простых случаев, но он очень гибкий и поддерживает сложную логику сортировки, особенно когда вам нужно часто сортировать одни и те же данные по одним и тем же правилам.
Выбор оптимального метода сортировки зависит от сложности критериев, частоты сортировки и требований к производительности. 📊
Сортировка списков словарей в Python — это гораздо больше, чем просто технический навык. Это мощный инструмент, позволяющий структурировать и анализировать данные, выявлять закономерности и представлять информацию в удобном виде. Освоив пять рассмотренных методов — от простейшей сортировки с lambda-функциями до продвинутых многоуровневых подходов — вы получаете контроль над данными любой сложности. Помните, что правильно выбранный метод сортировки не только делает ваш код эффективнее, но и значительно улучшает его читаемость, что критически важно при командной работе и длительной поддержке проектов.