Чому набори Python не зберігають порядок вставки?


12

Нещодавно я здивовано виявив, що, хоча дикти гарантують збереження порядку вставки в Python 3.7+, набори не такі:

>>> d = {'a': 1, 'b': 2, 'c': 3}
>>> d
{'a': 1, 'b': 2, 'c': 3}
>>> d['d'] = 4
>>> d
{'a': 1, 'b': 2, 'c': 3, 'd': 4}
>>> s = {'a', 'b', 'c'}
>>> s
{'b', 'a', 'c'}
>>> s.add('d')
>>> s
{'d', 'b', 'a', 'c'}

Яке обґрунтування цієї різниці? Чи не застосовуються ті самі покращення ефективності, які призвели команду Python змінити реалізацію диктату і для наборів?

Я не шукаю покажчиків на впорядковані набори реалізацій або способів використання диктів як резервних даних для наборів. Мені просто цікаво, чому команда Python не зробила вбудовані набори зберігати порядок одночасно з диктами.


1
Чи відповідає це на ваше запитання? Чи має Python замовлений набір?
Михай Челару

1
Ні, я розумію, що в Python немає впорядкованого набору. Мені просто цікаво, чому це так, оскільки диски тепер упорядковані.
Барт Робінсон

4
Шаблони використання різні, тому вони оптимізовані для різних випадків використання. Поширена помилкова думка, що набори - це просто дикти з нульовими значеннями в CPython, це абсолютно неправильно: реалізація відрізняється. Якщо ваше питання не закривається, я можу розмістити детальну відповідь.
Вім

1
"Шаблони використання різні, тому вони оптимізовані для різних випадків використання." Я думаю, що хороша відповідь на це питання могла б розібратися в цьому. Питання полягає в тому, що робить два різних підходи оптимальними для відповідних випадків використання.
Карл Кнечтел

Зауважте, що PyPy використовує однакове замовлення для обох dictі setз 2.7.
MisterMiyagi

Відповіді:


10

Набори та дикти оптимізовані для різних випадків використання. Основне використання набору - це тест на швидке членство, який є порядочним. Для диктів вартість пошуку - найважливіша операція, і ключ, швидше за все, буде присутній. З наборами наявність або відсутність елемента невідомо заздалегідь, тому реалізацію набору потрібно оптимізувати як для знайденого, так і не знайденого випадку. Крім того, деякі оптимізації для загальних операцій із встановленням, таких як об'єднання та перетину, ускладнюють збереження впорядкованості заданих без погіршення продуктивності.

Незважаючи на те, що обидві структури даних засновані на хеші, загальне помилкове уявлення про те, що набори просто реалізуються як дикти з нульовими значеннями. Ще до компактної реалізації dict у CPython 3.6 реалізація набору та dict вже суттєво відрізнялася з невеликим повторним використанням коду. Наприклад, дикти використовують рандомізоване зондування, але набори використовують комбінацію лінійного зондування та відкритої адресації для покращення локальності кешу. Початковий лінійний зонд ( 9 кроків за замовчуванням у CPython) перевірятиме ряд суміжних пар ключів / хешів, покращуючи продуктивність за рахунок зниження витрат на обробку хеш-зіткнень - послідовний доступ до пам'яті дешевше, ніж розсіяні зонди.

Було б можливо в теорії , щоб змінити набір реалізації CPython, щоб бути схожими на компактний Dict, але на практиці є недоліки, і відомі розробники ядра були проти створення такої зміни.

Набори залишаються не упорядкованими. (Чому? Шаблони використання різні. Також різні способи реалізації.)

- Гвідо ван Россум

Набори використовують інший алгоритм, який не підходить для збереження порядку вставки. Операції, що встановлюються на набір, втрачають свою гнучкість та оптимізацію, якщо потрібно замовлення. Математика набору визначається через не упорядковані множини. Коротше кажучи, встановити впорядкування не найближчим часом.

- Реймонд Хеттінгер

Детальне обговорення того, чи слід ущільнювати набори для 3.7, та відповіді про те, чому було вирішено проти, можна знайти у списках розсилки python-dev.

Підсумовуючи, основні моменти полягають у тому, що шаблони використання різні (дикти впорядкування вставки, такі як ** kwargs корисні , менші для наборів), економія місця для компактних наборів є менш значущою (оскільки для цього є лише ключ та хеш-масив для ущільнення, на відміну від ключів, хешів та значень), і згадана лінійна оптимізація зондування в наборах несумісна з компактною реалізацією.

Я відтворять пост Реймонда, внизу якого висвітлено найважливіші моменти.

14 вересня 2016 року о 15:50 Ерік Сноу написав:

Тоді я зроблю те ж саме для наборів.

Якщо я не зрозумів неправильно, Реймонд виступив проти внесення подібних змін у встановлення.

Це вірно. Ось декілька думок на цю тему, перш ніж люди почнуть бігати.

  • Для компактного диктату економія простору була чистою виграшею, а додатковий простір, який споживали індекси, і перерозподіл масивів ключ / значення / хеш більше ніж компенсується покращеною щільністю масивів ключ / значення / хеш. Однак для наборів мережа виявилася набагато менш сприятливою, оскільки нам ще потрібні індекси та перерозподіл, але можна лише компенсувати космічну вартість, зменшивши лише два з трьох масивів. Іншими словами, ущільнення має більше сенсу, коли ви витратили місце для ключів, значень та хешей. Якщо ви втратите одну з цих трьох, вона перестає бути переконливою.

  • Шаблон використання для наборів відрізняється від диктів. Перший має більше ударів чи пропусків. Останній, як правило, має менше пропущених ключових пошукових запитів. Крім того, деякі оптимізації для операцій "встановити набір" ускладнюють збереження замовлень набору, не впливаючи на продуктивність.

  • Я пройшов альтернативний шлях до покращення поставленої продуктивності. Замість ущільнення (яке не мало великої виграші місця та спричиняло витрати на додаткову непряму), я додав лінійне зондування, щоб зменшити витрати на зіткнення та поліпшити кеш-ефективність. Це вдосконалення є несумісним із підходом компактності, який я виступав за словники.

  • Наразі побічний ефект упорядкування для словників не гарантований, тому передчасно наполягати на тому, що набори також впорядковані. Документи вже посилаються на рецепт створення OrdersSet ( https://code.activestate.com/recipes/576694/ ), але, схоже, кількість споживань майже дорівнює нулю. Крім того, тепер, коли Ерік Сноу дав нам швидкий OrdersDict, простіше, ніж будь-коли, побудувати OrdersSet з MutableSet і OrрадDict, але я знову не спостерігав реального інтересу, оскільки типова аналітика даних "встановити на встановлення" насправді не є потреба чи турбота про замовлення. Аналогічно, основне використання тестів на швидке членство - це порядок агностики.

  • Однак, я думаю, що є можливість додати альтернативні варіанти наборів до PyPI. Зокрема, є декілька цікавих спеціальних випадків для впорядкованих даних, у яких операції встановлення на встановлення можна прискорити, порівнюючи цілі діапазони клавіш (див. Https://code.activestate.com/recipes/230113-implementation-of- набори з використанням-сортування-списки для початкової точки). IIRC, PyPI вже має код для набірних фільтрів цвітіння та хешування зозулі.

  • Я розумію, що головний блок коду прийнято в ядро ​​Python, але це не повинно відкривати шлюзам для залучення до більш великих переписувань інших типів даних, якщо ми не впевнені, що це гарантовано.

- Реймонд Хеттінгер

З [Python-Dev] Python 3.6 dict стає компактним і отримує приватну версію; і ключові слова впорядковуються , вересень 2016 року


2

Обговорення

Ваше запитання є германським і вже давно активно обговорювалося на python-devs . Р. Хеттінгер поділився переліком обґрунтування цієї теми . Стан питання з’являється відкритим зараз, незабаром після цієї детальної відповіді від Т. Петерса.

Коротше кажучи, реалізація сучасних диктів, що зберігають порядок вставки, унікальна і не вважається доцільною із наборами. Зокрема, дикти використовуються скрізь для запуску Python (наприклад, __dict__в просторах імен об'єктів). Головною мотивацією сучасного диктату було зменшення розміру, зробивши Python загалом ефективнішим для пам'яті. Навпаки, набори є менш поширеними, ніж дикти в ядрі Python, і, таким чином, переконують таке рефакторинг. Дивіться також розмову Р. Хеттінгера про реалізацію сучасного дикту.


Перспективи

Непорядкований характер множин у Python паралельний поведінці математичних множин . Замовлення не гарантується.

Відповідна математична концепція є не упорядкованою, і було б дивно нав'язувати таке, як наказ - Р. Хеттінгер

Якби будь-який порядок вводився до наборів у Python, то така поведінка відповідала б абсолютно окремій математичній структурі, а саме впорядкованому набору (або Oset). Осети грають окремий ролик з математики, особливо з комбінаторики. Одне практичне застосування Osets спостерігається при зміні дзвонів .

Маючи не упорядковані набори, узгоджуються із дуже загальною та всюдисущою структурою даних, яка відключає найсучаснішу математику, тобто теорію наборів . Я вважаю, непорядні набори в Python добре мати.

Дивіться також пов’язані публікації з цієї теми:

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