Это скорее ответ на 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, за исключением больших наборов (50 тыс. Элементов), размер которых изменяется в степени двух: (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
реализован довольно просто по внешнему виду: он пересекает список и выдает первый.
Это все детали реализации.