Почему словари python необратимы для python3.7?

11

Начиная с 3.7, стандартные словари python гарантированно поддерживают порядок вставки. (*)

d = {'b': 1, 'a': 2}
for k in d: 
    print(k)
# Prints always 'b' before 'a'.

Другими словами, ключи dict хранятся в строгом порядке. В принципе это позволило бы сделать ключи обратимыми. Тем не менее, ни одна из следующих работ:

# TypeError: 'dict' object is not reversible
for k in reversed(d): 
    print(k)

# TypeError: 'dict_keys' object is not reversible
for k in reversed(d.keys()): 
    print(k)

Вопросы: в чем причина такого поведения? Почему дикты не стали обратимыми? Есть ли планы изменить это поведение в будущем?

Обходной путь конечно работает:

for k in reversed(list(d.keys())): 
    print(k)

(*) На самом деле, это уже имеет место для типичных установок Python 3.6, как обсуждалось в этом посте .


Обновление : Начиная с python 3.8, дикты фактически обратимы. Принятый ответ относится к дискуссии между Гвидо и другими основными разработчиками, которая привела к этому решению. В двух словах, они оценили согласованность языков с усилиями по внедрению и реальными преимуществами для пользователей.

normanius
источник

Ответы:

5

Из документов :

перевернутый ( сек )

Вернуть реверс iterator. seq должен быть объектом, который имеет __reversed__()метод или поддерживает протокол последовательности ( __len__()метод и __getitem__()метод с целочисленными аргументами, начинающимися с 0).

dictОбъект не реализует __reversed__. Он реализует оба последних метода. Однако в __getitem__качестве аргументов используются ключи, а не целые числа (начиная с 0).

Что касается того, почему, это уже было предложено и обсуждено здесь .

РЕДАКТИРОВАТЬ:

Эти цитаты взяты из списка рассылки Python-Dev (поток «Добавить __reversed__ методы для dict», начатый 25. 05. 18), я начну с «концептуальных» аргументов, первый из которых - от Антуана Питроу:

Ничего не стоит, что OrderedDict уже поддерживает reversed (). Аргумент может идти в обе стороны:

  1. dict в настоящее время похож на OrderedDict, поэтому он также должен поддерживать reversed ();

  2. вы можете использовать OrderedDict, чтобы явно указать, что вы заботитесь о порядке; Не нужно ничего добавлять, чтобы диктовать.

Я полагаю, что гарантированный порядок вставки для обычных диктовок является совершенно новым, поэтому потребуется некоторое время, чтобы понятие обосновалось и стало частью повседневной мысли о диктатах. Как только это произойдет, вероятно, возникнут случаи использования, и в какой-то момент будет добавлено __reversed__. Реализация кажется простой, и это не слишком большой концептуальный скачок, чтобы ожидать, что конечный упорядоченный набор будет обратимым.

Вслед за ответом Раймонда Хеттингера:

Учитывая, что дикты теперь отслеживают порядок вставки, кажется разумным хотеть знать самые последние вставки (то есть циклически повторять самые последние добавленные задачи в dict задачи). Другие возможные варианты использования, вероятно, будут соответствовать тому, как мы используем хвостовую команду Unix.

Если возникают такие варианты использования, было бы неплохо, чтобы __reversed__ уже поддерживался, чтобы у людей не было соблазна внедрить уродливый обходной путь с помощью вызовов popitem () с последующими повторными вставками.

Основная проблема, выраженная в списке рассылки, заключалась в том, что это добавило бы слишком много раздувания или уменьшило бы эффективность памяти (необходимость иметь двусвязные списки вместо односвязных) хотя бы в некоторых реализациях, вот цитата Инады Наоки из системы отслеживания ошибок Python ( выпуск 33462). ):

«Иметь заказ» не означает «обратимый». Например, один связанный список упорядочен, но не обратим.

В то время как реализация CPython может обеспечить эффективность __reverse__, добавление __reverse__означает, что все реализации Python, как ожидается, предоставят ее. Например, некоторые реализации Python могут быть в состоянии реализовать dict с помощью hashmap + один связанный список. Если __reverse__добавлено, это больше невозможно.

Вернуться к списку рассылки, вот два последних сообщения (оба опубликованы 08.06.2018). Первый от Михаила Селика:

Правильно ли я сказал, что консенсус равен +1 для включения в v3.8?

Последним пунктом в этой теме было INADA Naoki, которая исследовала различные реализации и решила, что можно включить эту функцию в 3.8. Насколько я понимаю, Гвидо согласился с рекомендацией INADA подождать, пока MicroPython реализует v3.7. Поскольку ИНАДА изменила свое мнение, я полагаю, это все в пользу?

В заключение с сообщением Гвидо ван Россума:

Это звучит правильно для меня. Тогда у нас будет две версии, где это было так:

  • 3.6, где сохранение порядка было реализовано в CPython, но в спецификации языка

  • 3.7, где он также был добавлен в спецификацию языка

Как отмечено в другом ответе и комментариях, reversed()поддерживается как для диктовок, так и для dictviews, начиная с версии 3.8 (14.10.2018).

звездное гринвичское время
источник
5
Ваша вторая цитата выглядит как предвзятая подборка из этой темы. Похоже, консенсус заключается в том, что функциональность будет добавлена ​​в 3.8. Также до Python 3.7 просто не было упорядочения на нормальном dictобъекте (по крайней мере, не гарантировано языком), поэтому reversedтакже не имело смысла
FlyingTeller
У меня нет собаки в бою, я просто процитировал его первый ответ. Но вы правы, точка хорошо принята - я убрал цитату.
GST
1
Спасибо. Тема обсуждения Python-Dev была показательной. На самом деле, эта функция уже была реализована в Python 3.8, который был выпущен всего два дня назад (14 октября 2019 года).
Норманиус
Цитата docs бесполезна, она только напрашивается на следующий вопрос «так почему же не реализован тип dict __reversed__»? Ссылка на python-dev имеет полезный контент, но соответствующие части должны быть воспроизведены непосредственно в ответе (поскольку такие сторонние ссылки имеют тенденцию гнить).
Вим
1

Обновление для Python 3.8

Dict и dictviews теперь итерируемы в обратном порядке вставки, используя reversed ()

>>> dict = {1: "1", 2: "2", 3: "3"}
>>> reversed(dict)
<dict_reversekeyiterator object at 0x7f72ca795130>
Гончаров Дмитрий
источник
Приносит ли этот ответ что-то новое, что еще не охвачено другими ответами, комментариями или самим вопросом?
Норманиус
@normanius У этого есть небольшой визуальный пример кода, может быть полезным для быстрого браузера
jamylak