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

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

Ответ 1

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

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, поэтому возможны столкновения, даже если хеши разные, в зависимости от размера базовой таблицы).

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

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

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

Ответ 2

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

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

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

Ответ 3

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

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

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

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