Встроенный в 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 80

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 представляет unsigned 2919134373 и подпись -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 или написать свою собственную хэш-функцию.