Упорядочиваем словари в Python: сохраняем порядок элементов

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Для сохранения порядка ключей/значений в словарях Python 3.7+ можно использовать стандартный тип dict. В Python версий до 3.6 для этой цели подходит collections.OrderedDict, который точно так же гарантирует сохранение порядка.

Python
Скопировать код
# 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)])
Кинга Идем в IT: пошаговый план для смены профессии

Подробнее о сохранении порядка в словарях 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.
Python
Скопировать код
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 можно представить в виде библиотекаря (👩‍🏫), который аккуратно упорядочивает свою библиотеку (📚). Каждая книга (📕) в этой библиотеке — это уникальный ключ, а ее содержимое — значение:

Python
Скопировать код
OrderedDict([('Мистика', 'Шерлок Холмс'), ('Фэнтези', 'Гарри Поттер')])
# 👩‍🏫📚📕 -> 🕵️‍♂️ || 👩‍🏫📚📕 -> 🧙‍♂️

Библиотекарь следит, чтобы каждая новая книга занимала свое место в порядке:

До добавления: [📕🕵️‍♂️, 📕🧙‍♂️]

После добавления: [📕🕵️‍♂️, 📕🧙‍♂️, 📕👽] ('Научная фантастика' -> 'История пришельцев')

Таким образом, с OrderedDict порядок ключей и значений сохраняется в исходном виде.

Практические сценарии использования упорядоченных словарей

Конвертация между dict и OrderedDict

В Python 3.7+ можно преобразовывать обычный dict в OrderedDict, сохраняя порядок элементов. Однако в более старых версиях преобразование OrderedDict обратно в dict может вызвать изменение порядка.

Обратный порядок в OrderedDict

Захотели изменить направление? OrderedDict поддерживает итерацию в обратном порядке с помощью функции reversed(). Это позволяет обходить элементы в порядке, обратном их добавлению.

Python
Скопировать код
ordered = OrderedDict((f'item{i}', i) for i in range(5)) 
for item in reversed(ordered):
    print(item, ordered[item])  # Итерация в обратном порядке

Фиксируем порядок с помощью метода fromkeys

Если вам нужно зафиксировать порядок ключей, метод fromkeys позволяет создать OrderedDict со встроенным набором ключей и значениями по умолчанию, фиксируя порядок элементов.

Python
Скопировать код
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, есть множество рецептов создания упорядоченных коллекций.

Полезные материалы

  1. What’s New In Python 3.7 — Python 3.12.2 documentation — описание нововведений Python 3.7, включая поддержку порядка элементов в словарях.
  2. PEP 468 – Preserving the order of **kwargs in a function. | peps.python.org — описание сохранения порядка именованных аргументов в функциях начиная с Python 3.6.
  3. Dicts are now ordered, get used to it — статья о том, как начиная с версии 3.6 в Python словари стали упорядоченными.
  4. collections — Container datatypes — Python 3.12.2 documentation — документация по collections.OrderedDict, полезна для разработки на более старых версиях Python.
  5. Raymond Hettinger – Modern Python Dictionaries – A confluence of a dozen great ideas – PyCon 2017 – YouTube — анализ реализации словарей в Python от эксперта Рэймонда Хеттингера.
  6. General Python FAQ — Python 3.12.2 documentation — ответы на общие вопросы, в том числе о порядке элементов в словарях.
  7. Medium — сравнение OrderedDict и dict в Python и их использование в различных условиях.