Встроенная функция Python hash()

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)?

Ответ 1

Используйте hashlib, поскольку hash() был предназначен для использования к:

быстро сравнивать словарные ключи во время поиска словаря

и, следовательно, не гарантирует, что он будет одинаковым для реализации Python.

Ответ 2

Как указано в документации, встроенная функция hash() не предназначена для хранения полученных хэшей где-то снаружи. Он используется для предоставления хеш-значения объекта, для хранения их в словарях и т.д. Он также специфичен для реализации (GAE использует модифицированную версию Python). Выезд:

>>> class Foo:
...     pass
... 
>>> a = Foo()
>>> b = Foo()
>>> hash(a), hash(b)
(-1210747828, -1210747892)

Как вы можете видеть, они разные, поскольку hash() использует метод object __hash__ вместо "нормальных" алгоритмов хеширования, таких как SHA.

Учитывая вышеизложенное, рациональный выбор заключается в использовании модуля hashlib.

Ответ 3

Ответ абсолютно не удивляет: на самом деле

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.

Ответ 4

Большинство ответов подсказывают, что это из-за разных платформ, но для этого есть больше. Из документация object.__hash__(self):

По умолчанию значения __hash__() str, bytes и Объекты datetime "соленые" с непредсказуемым случайным значением. Хотя они остаются постоянными в рамках отдельного процесса Python, они не предсказуемы между повторными вызовами Python.

Это предназначено для обеспечения защиты от отказа в обслуживании вызванные тщательно подобранными входами, которые используют наихудший случай производительность вставки в форму, сложность O (n²). Видеть 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 share hash values.

Целое число должно быть десятичным числом в диапазоне [0, 4294967295]. Указание значения 0 отключит хеш-рандомизацию.

Например:

$ export PYTHONHASHSEED=0                            
$ python -c "print(hash('http://stackoverflow.com'))"
-5843046192888932305
$ python -c "print(hash('http://stackoverflow.com'))"
-5843046192888932305

Ответ 5

Результаты хэши варьируются между 32-битными и 64-битными платформами.

Если вычисленный хэш должен быть одинаковым на обеих платформах, используйте

def hash32(value):
    return hash(value) & 0xffffffff

Ответ 6

В предположении, что AppEngine использует 64-битную реализацию Python (-5768830964305142685 не поместится в 32 бита), а ваша реализация Python - 32 бита. Вы не можете полагаться на хеши объектов, которые значимо сопоставимы между различными реализациями.

Ответ 7

Это хэш-функция, которую 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

Ответ 8

Как насчет знакового бита?

Например:

Hex value 0xADFE74A5 представляет unsigned 2919134373 и подписан -1375832923. Значение currect должно быть подписано (знак бит = 1), но python преобразует его как unsigned, и мы имеем неправильное значение хэша после перевода с 64 до 32 бит.

Будьте осторожны, используя:

def hash32(value):
    return hash(value) & 0xffffffff

Ответ 9

Полиномиальный хеш для строк. 1000000009 и 239 - произвольные простые числа. Вряд ли столкновения произойдут случайно. Модульная арифметика не очень быстро, но для предотвращения столкновений это более надежно, чем при ее модуле с мощностью 2. Конечно, легко найти столкновение.

mod=1000000009
def hash(s):
    result=0
    for c in s:
        result = (result * 239 + ord(c)) % mod
    return result % mod

Ответ 10

Значение PYTHONHASHSEED может использоваться для инициализации хэш-значений.

Try:

PYTHONHASHSEED python -c 'print(hash('http://stackoverflow.com'))'

Ответ 11

Вероятно, он просто запрашивает операционную систему, а не собственный алгоритм.

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