Являются ли словари упорядоченными в Python 3.6+?

Словари упорядочены в Python 3.6 (по крайней мере, при реализации CPython), в отличие от предыдущих воплощений. Это похоже на существенное изменение, но это лишь короткий абзац в документации. Он описывается как деталь реализации CPython, а не язык, но также подразумевает, что это может стать стандартом в будущем.

Как новая реализация словаря работает лучше старой, сохраняя порядок элементов?

Вот текст из документации:

dict() теперь использует "компактное" представление впервые предложенное PyPy. Использование памяти dict() на 20% и 25% меньше по сравнению с Python 3.5. PEP 468 (Сохранение порядка ** kwargs в функции.) реализуется этим. Аспект сохранения порядка этой новой реализации рассматривается как деталь реализации и не следует полагаться (это может измениться в будущем, но желательно, чтобы эта новая реализация dict на языке для нескольких выпусков перед изменением спецификации языка для мандатной семантики сохранения порядка для всех текущих и будущих реализаций Python, что также помогает сохранить обратную совместимость со старыми версиями языка, на котором по-прежнему действует случайный порядок итераций, например Python 3.5). (Внесенный INADA Naoki в выпуск 27350 Идея изначально предложенная Раймондом Хеттингером.)

Обновление в декабре 2017 года: dict сохранение порядка вставки гарантировано для Python 3.7

Ответы

Ответ 1

Являются ли словари упорядоченными в Python 3.6 +?

Они вставляются упорядоченные [1]. Начиная с Python 3.6, для реализации Python на CPython словари запоминают порядок вставленных элементов. Это рассматривается как деталь реализации в Python 3.6; вам нужно использовать OrderedDict, если вы хотите, чтобы порядок вставки был гарантирован для других реализаций Python (и другого упорядоченного поведения [1]).

Начиная с Python 3.7, это уже не деталь реализации, а вместо этого становится языковой функцией. Из сообщения python-dev от GvR:

Сделайте так. "Dict сохраняет порядок вставки" - это решение. Спасибо!

Это просто означает, что вы можете зависеть от него. Другие реализации Python также должны предлагать вставляемый упорядоченный словарь, если они хотят быть совместимой реализацией Python 3.7.


Как реализуется реализация словаря Python 3.6 лучше [2] чем предыдущая, сохраняя порядок элементов?

По существу, сохраняя два массива.

  • Первый массив, dk_entries, содержит записи (типа PyDictKeyEntry) для словаря в том порядке, в котором они были вставлены. Сохранение порядка достигается тем, что это добавляет только массив, в котором новые элементы всегда вставлены в конце (порядок вставки).

  • Второй, dk_indices содержит индексы для массива dk_entries (то есть значения, указывающие позицию соответствующей записи в dk_entries). Этот массив действует как хеш-таблица. Когда ключ хэшируется, он приводит к одному из индексов, хранящихся в dk_indices, и соответствующая запись извлекается индексированием dk_entries. Поскольку сохраняются только индексы, тип этого массива зависит от общего размера словаря (от int8_t (1 byte ) на int32_t/int64_t (/ 8 байты) в битовых строках 32/64)

В предыдущей реализации должен был быть выделен разреженный массив типа PyDictKeyEntry и размер dk_size; к сожалению, это также привело к большому количеству пустого пространства, так как для производительности не хватило больше 2/3 * dk_size по причинам производительности. (а пустое пространство по-прежнему имело размер PyDictKeyEntry!).

Теперь это не так, поскольку хранятся только необходимые записи (те, которые были вставлены), и разреженный массив типа intX_t (X в зависимости от размера dict) 2/3 * dk_size full. Пустое пространство изменилось с типа PyDictKeyEntry на intX_t.

Итак, очевидно, что создание разреженного массива типа PyDictKeyEntry гораздо более требовательнее к памяти, чем разреженный массив для хранения int s.

Вы можете увидеть полный диалог на Python-Dev относительно этой функции, если это интересно, это хорошее чтение.


В оригинальном предложении, сделанном Раймондом Хеттингером, можно увидеть визуализацию используемых структур данных, которая отражает суть идеи.

Например, словарь:

d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}

в настоящее время хранится как:

entries = [['--', '--', '--'],
           [-8522787127447073495, 'barry', 'green'],
           ['--', '--', '--'],
           ['--', '--', '--'],
           ['--', '--', '--'],
           [-9092791511155847987, 'timmy', 'red'],
           ['--', '--', '--'],
           [-6480567542315338377, 'guido', 'blue']]

Вместо этого данные должны быть организованы следующим образом:

indices =  [None, 1, None, None, None, 0, None, 2]
entries =  [[-9092791511155847987, 'timmy', 'red'],
            [-8522787127447073495, 'barry', 'green'],
            [-6480567542315338377, 'guido', 'blue']]

Как вы теперь можете визуально видеть, в исходном предложении много места практически пусто, чтобы уменьшить количество конфликтов и ускорить поиск. С новым подходом вы уменьшаете память, необходимую для перемещения разреженности там, где это действительно необходимо, в индексы.


<суб > [1]: Я говорю "упорядочивание вставки", а не "упорядоченное", поскольку с "OrderedDict" "упорядочено" предлагает дальнейшее поведение, которое объект dict не предоставляет. OrderedDicts являются обратимыми, обеспечивают методы, чувствительные к порядку, и, в основном, обеспечивают тесты с проверкой порядка (==, !=). dict в настоящее время не предлагают ни одного из этих способов/методов. Суб >


<суб > [2]: Новые реализации словарей лучше выполняют память, будучи более компактными; что основное преимущество здесь. Скорость мудрая, разница не настолько резкая, там места, где новый dict может ввести небольшие регрессии (ключевые поисковые запросы, например), в то время как в других (итерация и изменение размера приходят на ум) должно быть повышение производительности. Суб >

<суб > В целом, производительность словаря, особенно в реальных ситуациях, улучшается благодаря введенной компактности.
Суб >

Ответ 2

Ниже отвечает первоначальный первый вопрос:

Должен ли я использовать dict или OrderedDict в Python 3.6?

Я думаю, что это предложение из документации на самом деле достаточно, чтобы ответить на ваш вопрос

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

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

Сделайте свой код в будущем доказательством:)

Здесь обсуждается вопрос о здесь.

EDIT: Python 3.7 сохранит это как функцию см.

Ответ 4

Я хотел бы добавить к обсуждению выше, но у меня нет репутации, чтобы комментировать.

Python 3.8 еще не совсем выпущен, но он даже будет включать функцию reversed() в словарях (устраняя еще одно отличие от OrderedDict.

Dict и dictviews теперь итерируемы в обратном порядке вставки, используя reversed(). (Предоставлено Rémi Lapeyre в bpo-33462.) Посмотрите, что нового в Python 3.8

Я не вижу никаких упоминаний об операторе равенства или других особенностях OrderedDict, поэтому они все еще не совсем одинаковы.