Упорядочиваем словари в Python: сохраняем порядок элементов
Быстрый ответ
Для сохранения порядка ключей/значений в словарях Python 3.7+ можно использовать стандартный тип dict
. В Python версий до 3.6 для этой цели подходит collections.OrderedDict
, который точно так же гарантирует сохранение порядка.
# Python 3.7+
my_dict = {'apple': 1, 'banana': 2}
print(my_dict) # Вывод: {'apple': 1, 'banana': 2}
# Python 3.6 и более ранние версии
from collections import OrderedDict
my_ordered_dict = OrderedDict([('apple', 1), ('banana', 2)])
print(my_ordered_dict) # Вывод: OrderedDict([('apple', 1), ('banana', 2)])
Подробнее о сохранении порядка в словарях Python
C Python 3.7 словари типа dict
поддерживают порядок добавления элементов. Он стал неотъемлемой частью языка.
В Python 3.6 порядок сохранялся благодаря особенностям реализации этих словарей. Это не было официальной гарантией языка, и порядок мог бы измениться в последующих версиях. Зато в Python 3.7 это поведение окончательно закреплено в языке.
Одним из побочных эффектов новой реализации словарей стало снижение потребления памяти за счет более компактной внутренней структуры. Это, к счастью, также помогает сохранять порядок, и создатели языка решили включить эту особенность в стандарт Python навсегда.
Когда стоит использовать OrderedDict
Несмотря на то, что стандартные словари сохраняют порядок добавления элементов, бывают ситуации, когда лучше использовать OrderedDict
:
- Совместимость с более ранними версиями Python: Если ваш код работает на версиях до 3.7, вам подойдет
OrderedDict
. - Ясность намерений: Когда в программе используется
OrderedDict
, это явно указывает на важность сохранения порядка элементов. - Дополнительные методы:
OrderedDict
предлагает некоторые методы, такие какpopitem(last=True)
для работы в порядке FIFO илиpopitem(last=False)
для порядка LIFO, а такжеmove_to_end()
, которых нет в типовомdict
.
from collections import OrderedDict
# Удаление первого элемента (FIFO)
fifo_dict = OrderedDict(one=1, two=2, three=3)
first_in, _ = fifo_dict.popitem(last=False)
print(f'Первый добавленный элемент: {first_in}')
# Удаление последнего элемента (LIFO)
lifo_dict = OrderedDict(one=1, two=2, three=3)
last_in, _ = lifo_dict.popitem()
print(f'Последний добавленный элемент: {last_in}')
Хранилища данных с упорядоченными ключами/значениями
Словари идеально подходят для реализации структур данных, где каждому ключу соответствует значение. Однако, если вам нужно строгое сохранение порядка, стоит использовать списки кортежей или специализированные классы, поскольку словари не предназначены для постоянного порядка элементов. Но заметьте, что, в отличие от словарей, списки не гарантируют быстрый поиск элементов.
Визуализация
OrderedDict
можно представить в виде библиотекаря (👩🏫), который аккуратно упорядочивает свою библиотеку (📚). Каждая книга (📕) в этой библиотеке — это уникальный ключ, а ее содержимое — значение:
OrderedDict([('Мистика', 'Шерлок Холмс'), ('Фэнтези', 'Гарри Поттер')])
# 👩🏫📚📕 -> 🕵️♂️ || 👩🏫📚📕 -> 🧙♂️
Библиотекарь следит, чтобы каждая новая книга занимала свое место в порядке:
До добавления: [📕🕵️♂️, 📕🧙♂️]
После добавления: [📕🕵️♂️, 📕🧙♂️, 📕👽] ('Научная фантастика' -> 'История пришельцев')
Таким образом, с OrderedDict
порядок ключей и значений сохраняется в исходном виде.
Практические сценарии использования упорядоченных словарей
Конвертация между dict
и OrderedDict
В Python 3.7+ можно преобразовывать обычный dict
в OrderedDict
, сохраняя порядок элементов. Однако в более старых версиях преобразование OrderedDict
обратно в dict
может вызвать изменение порядка.
Обратный порядок в OrderedDict
Захотели изменить направление? OrderedDict
поддерживает итерацию в обратном порядке с помощью функции reversed(). Это позволяет обходить элементы в порядке, обратном их добавлению.
ordered = OrderedDict((f'item{i}', i) for i in range(5))
for item in reversed(ordered):
print(item, ordered[item]) # Итерация в обратном порядке
Фиксируем порядок с помощью метода fromkeys
Если вам нужно зафиксировать порядок ключей, метод fromkeys
позволяет создать OrderedDict
со встроенным набором ключей и значениями по умолчанию, фиксируя порядок элементов.
keys = ('apple', 'banana', 'cherry')
frozen_dict = OrderedDict.fromkeys(keys, None)
print(frozen_dict)
# Вывод: OrderedDict([('apple', None), ('banana', None), ('cherry', None)])
Исторические справки и источники
Если хотите узнать больше, советуем обратиться к публикациям и докладам Реймонда Хеттингера, которые подробно описывают реализацию словарей в Python 3.6+. Для Python 2.7, где впервые был представлен OrderedDict
, есть множество рецептов создания упорядоченных коллекций.
Полезные материалы
- What’s New In Python 3.7 — Python 3.12.2 documentation — описание нововведений Python 3.7, включая поддержку порядка элементов в словарях.
- PEP 468 – Preserving the order of **kwargs in a function. | peps.python.org — описание сохранения порядка именованных аргументов в функциях начиная с Python 3.6.
- Dicts are now ordered, get used to it — статья о том, как начиная с версии 3.6 в Python словари стали упорядоченными.
- collections — Container datatypes — Python 3.12.2 documentation — документация по
collections.OrderedDict
, полезна для разработки на более старых версиях Python. - Raymond Hettinger – Modern Python Dictionaries – A confluence of a dozen great ideas – PyCon 2017 – YouTube — анализ реализации словарей в Python от эксперта Рэймонда Хеттингера.
- General Python FAQ — Python 3.12.2 documentation — ответы на общие вопросы, в том числе о порядке элементов в словарях.
- Medium — сравнение
OrderedDict
иdict
в Python и их использование в различных условиях.