Python 3.41 набор

У меня есть два вопроса о наборах.

1. Итак, когда я читаю, наборы неупорядочены, но когда я начал экспериментировать с ними, я обнаружил, что на самом деле существует какая-то упорядоченность.

Как видите, в этом наборе нет ничего особенного:

>>> v_set ={88,11,1,33,21,3,7,55,37,8}
>>> v_set
{33, 1, 3, 37, 7, 8, 11, 21, 55, 88}

Но этот отличается

>>> g_set={7,5,11,1,4,13,55,12,2,3,6,20,9,10}
>>> g_set
{1, 2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 13, 20, 55}

Я думаю, это потому, что на этот раз я записал более близкие числа, и имело смысл устанавливать эти числа в порядке возрастания...?

2. И второй вопрос о pop(). Я читал, что нет способа контролировать, какое значение удаляется с помощью метода pop (), это совершенно произвольно. Ставка, когда я использую метод pop (), всегда (я никогда не видел по-другому) берет первый элемент с левой стороны в наборах.

Как вы видете:

>>> v_set
{33, 1, 3, 37, 7, 8, 11, 21, 55, 88}
>>> v_set.pop()
33
>>> v_set.pop()
1
>>> v_set.pop()
3
>>> v_set.pop()
37
>>> v_set.pop()
7
>>> v_set.pop()
8
>>> v_set.pop()
11
>>> v_set.pop()
21
>>> v_set.pop()
55

Так действительно ли это совершенно произвольно?

3 ответа

Решение

Обратите внимание, что порядок элементов зависит (также) от порядка вставок. Вы можете легко увидеть это, когда есть столкновения:

In [4]: class Bad:
   ...:     def __init__(self, val, hash_val):
   ...:         self.val = val
   ...:         self.hash_val = hash_val
   ...:     def __str__(self):
   ...:         return 'Bad({0.val}, {0.hash_val})'.format(self)
   ...:     __repr__ = __str__
   ...:     def __eq__(self, other):
   ...:         return self.val == other.val
   ...:     def __hash__(self):
   ...:         return self.hash_val

In [5]: b1 = Bad(1, 1)
   ...: b2 = Bad(2, 1)
   ...: b3 = Bad(3, 2)

In [6]: {b1, b2, b3}
Out[6]: {Bad(2, 1), Bad(3, 2), Bad(1, 1)}

In [7]: {b2, b1, b3}
Out[7]: {Bad(1, 1), Bad(3, 2), Bad(2, 1)}

Как вы можете видеть в Out[6] первый элемент Bad(2, 1) и последнее Bad(1, 1) пока в Out[7] во-первых Bad(1, 1) и последнее Bad(2, 1),

Если не было столкновений:

In [8]: b1 = Bad(1, 1)
   ...: b2 = Bad(2, 2)
   ...: b3 = Bad(3, 3)

In [9]: {b1, b2, b3}
Out[9]: {Bad(1, 1), Bad(2, 2), Bad(3, 3)}

In [10]: {b2, b1, b3}
Out[10]: {Bad(1, 1), Bad(2, 2), Bad(3, 3)}

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

Другими словами, значений недостаточно для определения порядка элементов set, даже если вы знаете, как они реализованы. Вы также должны знать порядок вставок.

В общем set s имеют четко определенный порядок внутри одного прогона интерпретатора (из-за рандомизации в python3.3+), однако, какой порядок используется, зависит от выполненных вставок (как значения, так и порядка, в котором они выполняются), и произвольно, то есть в python3.5 они могут изменить порядок без уведомления, поэтому вы не можете на него положиться.

Они могли бы действительно рандомизировать выходные данные, но это просто добавило бы издержки без какой-либо выгоды.

Это не совсем произвольно. Но это неважно.

Мы называем набор неупорядоченным, потому что вы, как пользователь или клиент этого кода, не должны зависеть от конкретного заказа. Однако, основываясь на деталях реализации набора, вполне вероятно, что есть какой-то порядок.

То же самое в отношении pop(), Весьма вероятно, что конкретная реализация Python, которую вы используете, имеет логику, которая приведет к явно детерминированным результатам. Тем не менее, ваш код может использоваться с интерпретатором Python, который использует другую реализацию. A random element это единственная гарантия, которую вы получаете от реализации.

Подводя итог, документация дает вам набор гарантий, которым будет следовать любая совместимая реализация python. Дополнительные эффекты, которые вы наблюдаете, являются деталями реализации и могут измениться в любое время.

Да, порядок произвольный по определению. Даже если элементы хранятся в отсортированном порядке, они все равно будут произвольными. "Произвольный" означает, что Python не обещает упорядочивать данные каким-либо особым образом. Поскольку память линейна, она должна использовать некоторый порядок, но вы никогда не должны полагаться на этот порядок, потому что он может быть изменен без уведомления. (На самом деле, в последних версиях Python порядок элементов в set частично рандомизирован.)

Ваш второй пример показывает, что порядок печати совпадает с порядком выталкивания. Это имеет смысл: repr ходит по предметам в том порядке, в котором они хранятся в памяти, и pop по-видимому, возвращает первый элемент в соответствии с тем же порядком. Опять же, вы не можете полагаться на это: это деталь реализации, и если разработчики Python найдут более быстрый способ сделать это popони могут взломать любой код, который полагается на set упорядоченность.

Если вы хотите узнать, как это работает, прочтите хеш-таблицы.

Другие вопросы по тегам