Встроенный в Python хэш-код() функция
Windows XP, Python 2.5:
hash('http://stackoverflow.com') Result: 1934711907
Google App Engine (http://shell.appspot.com/):
hash('http://stackoverflow.com') Result: -5768830964305142685
почему это? Как я могу иметь хэш-функцию, которая даст мне одинаковые результаты на разных платформах (Windows, Linux, Mac)?
11 ответов:
использовать hashlib как
hash()
был разработан для использования в:быстрое сравнение ключей словаря во время поиска словаря
и поэтому не гарантирует, что он будет одинаковым в реализациях Python.
как указано в документации, встроенная функция hash () является не предназначен для хранения результирующих хэшей где-то снаружи. Он используется для предоставления хэш-значения объекта, для хранения их в словарях и так далее. Это также специфично для реализации (GAE использует модифицированную версию Python). Проверьте:
>>> class Foo: ... pass ... >>> a = Foo() >>> b = Foo() >>> hash(a), hash(b) (-1210747828, -1210747892)
Как вы можете видеть, они разные, так как hash () использует объект
__hash__
метод вместо "нормальных" алгоритмов хэширования, таких как SHA.дали выше, рациональным выбором является использование hashlib модуль.
ответ абсолютно не удивляет: на самом деле
In [1]: -5768830964305142685L & 0xffffffff Out[1]: 1934711907L
так что если вы хотите получить достоверные ответы на строках ASCII, просто получите более низкие 32 бита как
uint
. Хэш-функция для строк является 32-разрядной безопасной и почти портативное.С другой стороны, вы не можете рассчитывать на получение
hash()
любой объект, над которым вы явно не определили__hash__
метод инварианта.над строками ASCII он работает только потому, что хэш вычисляется на отдельных символах, образующих строку, например:
class string: def __hash__(self): if not self: return 0 # empty value = ord(self[0]) << 7 for char in self: value = c_mul(1000003, value) ^ ord(char) value = value ^ len(self) if value == -1: value = -2 return value
здесь
c_mul
функция-это "циклическое" умножение (без переполнения), как в C.
большинство ответов предполагают, что это из-за разных платформ, но это еще не все. От документация
object.__hash__(self)
:по умолчанию
__hash__()
значенияstr
,bytes
иdatetime
объекты "соленые" с непредсказуемым случайным значением. Хотя они остаются постоянными в рамках отдельного процесса Python, они не предсказуемы между повторными вызовами Python.это предназначено для обеспечения защита от отказа в обслуживании вызвано тщательно подобранными входными данными, которые используют наихудший случай производительность вставки дикт, о(Н2) сложности. Видеть http://www.ocert.org/advisories/ocert-2011-003.html Подробнее.
изменение хэш-значений влияет на порядок итерации
dicts
,sets
и другие сопоставления. Python никогда не давал гарантий по этому поводу порядок (и он обычно варьируется между 32-разрядными и 64-разрядными опирающийся.)даже запуск на одной и той же машине даст различные результаты при вызовах:
$ python -c "print(hash('http://stackoverflow.com'))" -3455286212422042986 $ python -c "print(hash('http://stackoverflow.com'))" -6940441840934557333
время:
$ python -c "print(hash((1,2,3)))" 2528502973977326415 $ python -c "print(hash((1,2,3)))" 2528502973977326415
Смотрите также переменную окружения
PYTHONHASHSEED
:если эта переменная не установлена или установлена в
random
, используется случайное значение чтобы посеять хэшиstr
,bytes
иdatetime
объекты.если
PYTHONHASHSEED
устанавливается в целочисленное значение, оно используется как фиксированный семя для созданияhash()
из типов, охватываемых хэшем рандомизация.ее целью является повторимым хеширования, таких как тесты самодиагностики для сам переводчик, или разрешить скопление на Python процессов поделитесь хэш-значениями.
целое число должно быть целым десятичным числом в диапазоне
[0, 4294967295]
. Указание значения0
отключить хэш рандомизации.например:
$ export PYTHONHASHSEED=0 $ python -c "print(hash('http://stackoverflow.com'))" -5843046192888932305 $ python -c "print(hash('http://stackoverflow.com'))" -5843046192888932305
хэш-результаты варьируются между 32-битными и 64-битными платформами
Если вычисленный хэш должен быть одинаковым на обеих платформах рассмотрите возможность использования
def hash32(value): return hash(value) & 0xffffffff
по предположению, AppEngine использует 64-разрядную реализацию Python (-5768830964305142685 не поместится в 32 бита), и ваша реализация Python составляет 32 бита. Вы не можете полагаться на то, что хэши объектов будут значимо сопоставимы между различными реализациями.
это хэш-функция, которую Google использует в производстве для python 2.5:
def c_mul(a, b): return eval(hex((long(a) * b) & (2**64 - 1))[:-1]) def py25hash(self): if not self: return 0 # empty value = ord(self[0]) << 7 for char in self: value = c_mul(1000003, value) ^ ord(char) value = value ^ len(self) if value == -1: value = -2 if value >= 2**63: value -= 2**64 return value
как насчет знака бит?
например:
шестнадцатеричное значение
0xADFE74A5
представляет unsigned2919134373
и подпись-1375832923
. Значение Currect должно быть подписано (sign bit = 1), но python преобразует его как беззнаковое, и у нас есть неправильное значение хэша после перевода с 64 на 32 бит.будьте осторожны при использовании:
def hash32(value): return hash(value) & 0xffffffff
полиномиальный хэш для строк.
1000000009
и239
произвольные простые числа. Маловероятно, чтобы столкновения были случайными. Модульная арифметика не очень быстра, но для предотвращения столкновений это более надежно, чем принимать его по модулю степени2
. Конечно, легко найти столкновение нарочно.mod=1000000009 def hash(s): result=0 for c in s: result = (result * 239 + ord(c)) % mod return result % mod
значение PYTHONHASHSEED может использоваться для инициализации хэш-значений.
попробуй:
PYTHONHASHSEED python -c 'print(hash('http://stackoverflow.com'))'
Он, вероятно, просто просит операционную систему предоставить функцию, а не свой собственный алгоритм.
Как говорится в других комментариях, используйте hashlib или написать свою собственную хэш-функцию.