В моей ситуации я пытался запустить веб-сервис java в Tomcat 7 через разъем в Eclipse. Приложение работает хорошо, когда я развернул файл войны на экземпляр Tomcat 7 на моем ноутбуке. Для приложения требуется драйвер jdbc типа 2 для «IBM DB2 9.5». По какой-то нечетной причине соединитель в Eclispe не мог видеть или использовать пути в переменных среды IBM DB2, чтобы добраться до DLL-файлов, установленных на моем ноутбуке, как клиент jcc. В сообщении об ошибке либо указано, что ему не удалось найти файл db2jcct2 dll, либо ему не удалось найти зависимые библиотеки для этого DLL-файла. В конечном итоге я удалил соединитель и перестроил его. Затем он работал правильно. Я добавляю это решение здесь как документацию, потому что мне не удалось найти это конкретное решение в другом месте.
Вы должны посмотреть это видео (хотя это специфичный для 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 .
Одна ключевая вещь, которая намекается на отличный ответ 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.) Это именно то, что вы видите.
Конечно, стоит посмотреть на , как наборы фактически реализовано , а не гадать. Но то, что вы предполагаете, основываясь на предположении простейшей реализации хеш-таблицы, - это (запрет на столкновение и запрещение расширения хеш-таблицы), что именно происходит.
Причиной такого поведения является то, что 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.
Наборы Python AFAIK реализованы с использованием хэш-таблицы . Порядок, в котором элементы отображаются, зависит от используемой функции хэша. В рамках одного и того же запуска программы хеш-функция, вероятно, не изменяется, поэтому вы получаете тот же порядок.
Но нет никаких гарантий того, что он всегда будет использовать одну и ту же функцию, и порядок будет изменяться во всех прогонах - или в рамках одного и того же запуска, если вы вставляете множество элементов, а хэш-таблица должна изменять размер.
Наборы основаны на хэш-таблице. Хэш значения должен быть последовательным, поэтому порядок будет также - если два элемента хэш не совпадают с одним и тем же кодом, и в этом случае порядок вставки изменит порядок вывода.