В Python, что вы подразумеваете под & ldquo; set is unordered & rdquo ;? [Дубликат]

В моей ситуации я пытался запустить веб-сервис java в Tomcat 7 через разъем в Eclipse. Приложение работает хорошо, когда я развернул файл войны на экземпляр Tomcat 7 на моем ноутбуке. Для приложения требуется драйвер jdbc типа 2 для «IBM DB2 9.5». По какой-то нечетной причине соединитель в Eclispe не мог видеть или использовать пути в переменных среды IBM DB2, чтобы добраться до DLL-файлов, установленных на моем ноутбуке, как клиент jcc. В сообщении об ошибке либо указано, что ему не удалось найти файл db2jcct2 dll, либо ему не удалось найти зависимые библиотеки для этого DLL-файла. В конечном итоге я удалил соединитель и перестроил его. Затем он работал правильно. Я добавляю это решение здесь как документацию, потому что мне не удалось найти это конкретное решение в другом месте.

33
задан mgilson 27 November 2013 в 23:37
поделиться

5 ответов

Вы должны посмотреть это видео (хотя это специфичный для CPython1 и около словарей), но я предполагаю, что это относится и к наборам).

В принципе, python хэширует элементы и берет последние N бит (где N определяется размером набора) и использует эти биты в качестве индексов массива для размещения объекта в памяти. Затем объекты выводятся в том порядке, в котором они существуют в памяти. Конечно, изображение становится немного более сложным, когда вам нужно разрешать столкновения между хэшами, но это его суть.

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

Например:

list1 = [8,16,24]
set(list1)        #set([8, 16, 24])
list2 = [24,16,8]
set(list2)        #set([24, 16, 8])

Обратите внимание: тот факт, что порядок сохраняется в этих наборах, является «совпадением» и имеет отношение к разрешению конфликтов (о котором я ничего не знаю). Дело в том, что последние 3 бита hash(8), hash(16) и hash(24) совпадают. Поскольку они одинаковы, разрешение конфликтов берет на себя и помещает элементы в «резервные» ячейки памяти вместо первого (лучшего) выбора и поэтому определяется ли 8 местоположение или 16, по которому кто-то прибыл на сторону

Если мы повторим пример с 1, 2 и 3, вы получите последовательный порядок независимо от того, какой порядок у них есть в списке входных данных :

list1 = [1,2,3]
set(list1)      # set([1, 2, 3])
list2 = [3,2,1]
set(list2)      # set([1, 2, 3])

, поскольку последние 3 бита hash(1), hash(2) и hash(3) уникальны.


1Note Реализация, описанная здесь, применима к CPython dict и set. Я думаю, что общее описание действительно для всех современных версий CPython до 3.6. Однако, начиная с CPython3.6, есть дополнительная деталь реализации, которая фактически сохраняет порядок вставки для итерации для dict. Похоже, что set до сих пор не обладает этим свойством. Структура данных описывается этой записью в блоге людьми pypy (которые начали использовать это перед людьми CPython). Исходная идея (по крайней мере для экосистемы python) заархивирована в списке рассылки python-dev .

32
ответ дан mgilson 18 August 2018 в 19:01
поделиться
  • 1
    Хороший пример. К сожалению, это означает, что порядок будет таким же, как и порядок, и я не думаю, что это всегда будет так. – Mark Ransom 28 August 2012 в 19:30
  • 2
    @MarkRansom - Это не всегда так, но мне пришлось придумать пример, где я знал, что будут хеш-коллизии ... Я добавлю отказ. – mgilson 28 August 2012 в 19:31
  • 3
    @MarkRansom - добавлен другой (счетчик) пример, где хеши все уникальны, демонстрируя, что порядок набора согласован в этом сценарии. Спасибо, что указали, как это может быть путаным - надеюсь, теперь лучше. – mgilson 28 August 2012 в 19:41
  • 4
    Не то, чтобы это влияло на основные идеи, представленные здесь, но начиная с Python 3.3, хеш-рандомизация строк и дат включена по умолчанию (и доступна в последних обновлениях 2.6, 2.7, 3.1 и 3,2). Ваши примеры не должны быть затронуты, но пример строки OP может быть. – John Y 28 August 2012 в 20:02
  • 5
    @PadraicCunningham - Спасибо. Youtube, вероятно, более надежный, чем любой другой сайт. – mgilson 17 December 2015 в 00:53

Одна ключевая вещь, которая намекается на отличный ответ mgilson , но явно не упоминается ни в одном из существующих ответов:

Малые целые числа хеш для себя:

>>> [hash(x) for x in (1, 2, 3, 88)]
[1, 2, 3, 88]

Строки hash для значений, которые непредсказуемы. Фактически, с 3.3 по умолчанию, , они построены на семя, которое рандомизировано при запуске . Итак, вы получите разные результаты для каждого нового сеанса интерпретатора Python, но:

>>> [hash(x) for x in 'abcz']
[6014072853767888837,
 8680706751544317651,
 -7529624133683586553,
 -1982255696180680242]

Итак, рассмотрим простейшую возможную реализацию хеш-таблицы: просто массив из N элементов, где вставка значение означает, что он помещается в hash(value) % N (при отсутствии столкновений). И вы можете сделать приблизительное предположение о том, насколько велика N - она ​​будет немного больше, чем количество элементов в ней. При создании набора из последовательности из 6 элементов N легко может быть, например, 8.

Что происходит, когда вы храните эти 5 чисел с N = 8? Ну, hash(1) % 8, hash(2) % 8 и т. Д. Являются только самими числами, но hash(88) % 8 равно 0. Итак, массив хеш-таблицы заканчивается удерживанием 88, 1, 2, NULL, NULL, 5, NULL, 7. Поэтому должно быть легко понять, почему итерация набора может дать вам 88, 1, 2, 5, 7.

Конечно, Python не гарантирует , что вы получите этот заказ каждый раз. Небольшое изменение способа, которое он угадывает при правильном значении для N, может означать, что 88 заканчивается где-то другим (или заканчивается столкновением с одним из других значений). И, фактически, запуская CPython 3.7 на моем Mac, я получаю 1, 2, 5, 7, 88 .0

Между тем, когда вы создаете хэш из последовательности размером 11, а затем вставляете в него рандомизированные хэши, что происходит? Даже предполагая простейшую реализацию и предполагая, что нет столкновений, вы все еще не знаете, какой заказ вы собираетесь получить. Он будет согласован в течение одного прогона интерпретатора Python, но при следующем запуске он будет отличаться. (Если вы не установили PYTHONHASHSEED в 0 или какое-то другое значение int.) Это именно то, что вы видите.


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

0
ответ дан abarnert 18 August 2018 в 19:01
поделиться

Причиной такого поведения является то, что Python использует хеш-таблицы для реализации словаря: https://en.wikipedia.org/wiki/Hash_table#Open_addressing

Позиция ключ определяется его адресом памяти. Если вы знаете память повторного использования Python для некоторых объектов:

>>> a = 'Hello world'
>>> id(a)
140058096568768
>>> a = 'Hello world'
>>> id(a)
140058096568480

Вы можете видеть, что объект a имеет разные адреса каждый раз, когда он инициализирован.

Но для небольших целых чисел это не изменение :

>>> a = 1
>>> id(a)
40060856
>>> a = 1
>>> id(a)
40060856

Даже если мы создадим второй объект с другим именем, он будет таким же:

>>> b = 1
>>> id(b)
40060856

Этот подход позволяет сэкономить память, которую использует интерпретатор Python.

4
ответ дан Eugene Soldatov 18 August 2018 в 19:01
поделиться

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

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

3
ответ дан MAK 18 August 2018 в 19:01
поделиться

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

2
ответ дан Mark Ransom 18 August 2018 в 19:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: