Чому словники python не є оборотними для python3.7?


11

Починаючи з 3.7, стандартні словники python гарантовано підтримують порядок вставки. (*)

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

Іншими словами, клавіші диктанту зберігаються в суворому порядку. В принципі, це дозволило б повернути ключі. Однак жодна з наступних робіт:

# 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 дикти насправді є оборотними. Прийнята відповідь стосується дискусії між Гідо та іншими розробниками основних процесів, що призвело до цього рішення. Коротше кажучи, вони зважили мовну послідовність щодо зусиль із впровадження та фактичної вигоди для користувачів.

Відповіді:


5

З документів :

перевернутий ( послідовно )

Повернути реверс iterator. seq повинен бути об'єктом, який має __reversed__()метод або підтримує протокол послідовності ( __len__()метод і __getitem__()метод з цілими аргументами, починаючи з 0).

dictОб'єкт не реалізує __reversed__. Він реалізує обидва останні методи. Однак __getitem__приймає ключі як аргументи, а не цілі числа (починаючи з 0).

Щодо того, це вже було запропоновано та обговорено тут .

Редагувати:

Ці цитати з списку розсилки Python-Dev (потік "Додати __reversed__ методів для dict", розпочато 25.05.18), я розпочну з "концептуальних" аргументів, спочатку з Антуана Пітру:

Нічого не варто, що OrdersDict вже підтримує reversed (). Аргумент може йти двома способами:

  1. dict схожий на OrdersDict в наші дні, тому він повинен підтримувати і зворотний ();

  2. ви можете використовувати OrdersDict для явного сигналу про те, що ви дбаєте про замовлення; не потрібно нічого додавати, щоб диктувати.

Думаю, що гарантований порядок вставки для звичайних диктів є абсолютно новим, тому пройде певний час, щоб поняття оселилося і стало частиною щоденного роздуму про дикти. Як тільки це станеться, ймовірно, неминуче з'являться випадки використання і в якийсь момент буде додано __reversed__. Реалізація здається простою, і очікувати на те, що кінцева замовлена ​​колекція буде оборотна, не так вже й багато концептуального стрибка.

Після цього відповідь Реймонда Хеттінгера:

Зважаючи на те, що тепер диктовки відстежують порядок вставки, представляється розумним бажати знати найсвіжіші вставки (тобто, перебираючи останні нещодавно додані завдання в диктаті завдань). Інші можливі випадки використання, ймовірно, відповідатимуть тому, як ми використовуємо хвостову команду Unix.

Якщо виникають такі випадки використання, було б добре, щоб __reversed__ вже був підтриманий, щоб люди не спокусилися здійснити некрасиве вирішення за допомогою викликів popitem () з подальшими повторними вставками.

Основне занепокоєння, висловлене в списку розсилки, полягало в тому, що це додасть занадто велику кількість потужностей або знизить ефективність пам’яті (необхідність мати подвійні зв’язані списки замість одноособово пов’язаних) принаймні в деяких реалізаціях, ось цитата Інади Наокі з трекера помилок Python ( випуск 33462 ):

"Мати замовлення" не означає "зворотний". Наприклад, єдиний зв'язаний список упорядкований, але не оборотний.

Хоча реалізація CPython може забезпечити ефективність __reverse__, додаючи __reverse__кошти, як очікується, все її реалізація Python забезпечує це. Наприклад, деяка реалізація Python може бути в змозі реалізувати dict за допомогою хешмапу + єдиний пов'язаний список. Якщо __reverse__додано, це вже неможливо.

Назад до списку розсилки, ось два останні повідомлення (обидва опубліковані 08.06.2018). Спочатку від Михайла Селіка:

Чи правильно я кажу, що консенсус +1 для включення в v3.8?

Останнім пунктом у потоці була INADA Naoki, яка досліджувала різні реалізації та вирішила, що нормально включати цю функцію в 3.8. Наскільки я розумію, Гуїдо погодився з порадою INADA чекати, коли MicroPython реалізує v3.7. Оскільки INADA передумав, я гадаю, це все на користь?

Завершуючи повідомленням Гідо ван Россума:

Це звучить мені правильно. Тоді ми мали б дві версії, де це було так:

  • 3.6, де збереження порядку здійснювалося в CPython, але в мовній специфікації

  • 3.7, де вона також була додана до мовної специфікації

Як зазначається в іншій відповіді та коментарях, reversed()підтримується як диктовками, так і диктовками з версії 3.8 (14.10.2018).


5
Ваша друга цитата здається вибігом зміщення з цієї теми. Здається, що консенсус полягає в тому, що функціональність буде додана в 3.8. Також до python 3.7 просто не було замовлення на звичайний dictоб’єкт (принаймні не гарантовано з мови), тому reversedтакож не було сенсу
FlyingTeller

У мене немає собаки в бійці, я просто цитував його першу відповідь. Але ти маєш рацію, вказуй, ​​що добре - я вилучив цитату.
gst

1
Дякую. Дискусійний потік пітона-розробника виявився розкритим. По суті, ця функція вже реалізована в python 3.8, який був випущений лише два дні тому (14 жовтня 2019 року).
Норманій


Цитата документів не є корисною, вона ставить лише наступне запитання "так, чому ж не застосовується тип диктату __reversed__"? Посилання python-dev має корисний вміст, але відповідні частини повинні бути відтворені у відповіді безпосередньо (оскільки такі посилання поза сайтом мають тенденцію до гниття).
Вім


1

Оновлення python 3.8

Dictview і dictviews тепер можна виправити у зворотному порядку вставки, використовуючи reversed ()

>>> dict = {1: "1", 2: "2", 3: "3"}
>>> reversed(dict)
<dict_reversekeyiterator object at 0x7f72ca795130>

Чи сприяє ця відповідь чимось новим, що вже не охоплене іншими відповідями, коментарями чи самим питанням?
Норманій

@normanius Він має зразок візуального коду lil, може бути корисним для швидкого браузера
jamylak
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.