Це більше відповідь на Python 3.41 Набір до його закриття як дублікат.
Інші праві: не покладайтеся на замовлення. Навіть не робіть вигляд, що є такий.
Однак, є одне, на що можна покластися:
list(myset) == list(myset)
Тобто порядок стабільний .
Розуміння того, чому існує сприйнятий порядок, вимагає розуміння кількох речей:
З вершини:
Хеш - набір являє собою спосіб зберігання випадкових даних з дуже швидким пошуком рази.
Він має резервний масив:
# A C array; items may be NULL,
# a pointer to an object, or a
# special dummy object
_ _ 4 _ _ 2 _ _ 6
Ми будемо ігнорувати спеціальний манекенний об’єкт, який існує лише для того, щоб видалити простіші справи з ними, оскільки ми не будемо видаляти з цих наборів.
Для того, щоб зробити дійсно швидкий пошук, ви зробите якусь магію, щоб обчислити хеш від об'єкта. Єдине правило - два об’єкти, які є рівними, мають однаковий хеш. (Якщо два об'єкти мають однаковий хеш, вони можуть бути нерівними.)
Потім ви робите індекс, беручи модуль за довжиною масиву:
hash(4) % len(storage) = index 2
Це робить дійсно швидким доступ до елементів.
Хеш тільки велика частиною історії, так hash(n) % len(storage)
і hash(m) % len(storage)
можуть привести до того ж номеру. У цьому випадку кілька різних стратегій можуть спробувати вирішити конфлікт. CPython використовує "лінійне зондування" 9 разів, перш ніж робити складні речі, тому він буде шукати ліворуч від слота до 9 місць, перш ніж шукати в іншому місці.
Хешові набори CPython зберігаються так:
Хеш-набір може бути не більше 2/3 . Якщо є 20 елементів, а резервний масив - 30 елементів, резервна копія змінить розмір, щоб бути більшим. Це відбувається тому, що ви стикаєтеся частіше з невеликими підкладками, а зіткнення сповільнюють усе.
Магазин резервного розміру змінює потужність 4, починаючи з 8, за винятком великих наборів (50k елементів), які змінюють розміри в два: (8, 32, 128, ...).
Отже, коли ви створюєте масив, зберігання резервного копіювання становить довжину 8. Коли він заповнений на 5, і ви додасте елемент, він коротко містить 6 елементів. 6 > ²⁄₃·8
таким чином, це запускає розмір, і резервна копія зберігається вчетверо до розміру 32.
Нарешті, hash(n)
просто повертається n
для чисел (за винятком -1
спеціальних).
Отже, давайте розглянемо перший:
v_set = {88,11,1,33,21,3,7,55,37,8}
len(v_set)
- 10, тому після додавання всіх предметів резервного зберігання принаймні 15 (+1) . Відповідна потужність 2 - 32. Отже, підкладка - це:
__ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
Ми маємо
hash(88) % 32 = 24
hash(11) % 32 = 11
hash(1) % 32 = 1
hash(33) % 32 = 1
hash(21) % 32 = 21
hash(3) % 32 = 3
hash(7) % 32 = 7
hash(55) % 32 = 23
hash(37) % 32 = 5
hash(8) % 32 = 8
тому вони вставляються як:
__ 1 __ 3 __ 37 __ 7 8 __ __ 11 __ __ __ __ __ __ __ __ __ 21 __ 55 88 __ __ __ __ __ __ __
33 ← Can't also be where 1 is;
either 1 or 33 has to move
Тож ми очікували, що таке замовлення
{[1 or 33], 3, 37, 7, 8, 11, 21, 55, 88}
з 1 або 33, що не знаходиться на старті десь в іншому місці. Для цього буде використано лінійне зондування, тож у нас буде або:
↓
__ 1 33 3 __ 37 __ 7 8 __ __ 11 __ __ __ __ __ __ __ __ __ 21 __ 55 88 __ __ __ __ __ __ __
або
↓
__ 33 1 3 __ 37 __ 7 8 __ __ 11 __ __ __ __ __ __ __ __ __ 21 __ 55 88 __ __ __ __ __ __ __
Ви можете очікувати, що 33 буде зміщений, оскільки 1 вже був там, але через зміну розміру, що відбувається під час створення набору, це насправді не так. Кожен раз, коли набір відновлюється, елементи, які вже додаються, фактично упорядковуються.
Тепер ви можете зрозуміти, чому
{7,5,11,1,4,13,55,12,2,3,6,20,9,10}
може бути в порядку. Є 14 елементів, тож накопичувач підкладки становить щонайменше 21 + 1, що означає 32:
__ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __
1 - 13 хешу в перших 13 слотах. 20 йде в слот 20.
__ 1 2 3 4 5 6 7 8 9 10 11 12 13 __ __ __ __ __ __ 20 __ __ __ __ __ __ __ __ __ __ __
55 є в слоті, hash(55) % 32
який становить 23:
__ 1 2 3 4 5 6 7 8 9 10 11 12 13 __ __ __ __ __ __ 20 __ __ 55 __ __ __ __ __ __ __ __
Якби ми обрали натомість 50, ми б очікували
__ 1 2 3 4 5 6 7 8 9 10 11 12 13 __ __ __ __ 50 __ 20 __ __ __ __ __ __ __ __ __ __ __
І ось ось:
{1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 20, 50}
#>>> {1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 50, 20}
pop
реалізується досить просто за зовнішнім виглядом речей: він переходить список і вискакує перший.
Це вся деталь реалізації.