2009-04-27 3 views
80

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

+14

это обязаны тем, ваш WinXP является 32-битной платформе в то время как Google, это 64 бит –

ответ

54

Использование hashlib в hash()was designed to be used to:

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

и, следовательно, не гарантирует, что она будет такой же по реализации Python.

+5

ли не хэш-функции в 'hashlib' немного медленно для не-криптографических использовать? –

+0

@ Брандон: они? тесты? патчи? – SilentGhost

+8

Они на самом деле очень медленные по сравнению с хэш-функциями общего назначения, такими как Дженкинс, Бернстайн, ФНВ, Мурмурхаш и многие другие. Если вы хотите создать свою собственную хеш-подобную структуру, я предлагаю посмотреть на uthash.h http://uthash.sourceforge.net/ – lericson

-3

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

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

88

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

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

Как вы можете видеть, они разные, как и хэш() использует __hash__ метод объекта вместо «нормальных» алгоритмов хеширования, такие как SHA.

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

+0

Спасибо! Я пришел сюда, задаваясь вопросом, почему я всегда получаю разные значения хэша для идентичных объектов, что приводит к неожиданному поведению с dicts (который индексируется по типу hash +, а не проверяет на равенство). Быстрый способ генерации собственного хэша int из hashlib.md5 - это 'int (hashlib.md5 (repr (self)). Hexdigest(), 16)' (предполагая, что 'self .__ repr__' был определен как идентичный iff objects идентичны). Если 32 байта слишком длинны, вы можете, конечно, сократить размер, разрезав шестнадцатеричную строку до преобразования. –

+1

С другой стороны, если '__repr__' является достаточно уникальным, вы можете просто использовать' str .__ hash__' (т. Е. 'Hash (repr (self))'), поскольку dicts не смешивает неравные объекты с одинаковым хэшем. Это работает только в том случае, если объект достаточно тривиален, и, очевидно, репрезент может представлять идентичность. –

+0

Итак, в вашем примере с двумя объектами 'a' и' b', как я могу использовать модуль hashlib, чтобы увидеть, что объекты идентичны? – Garrett

6

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

32

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

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.

8

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

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

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

насчет знаковому биту?

Например:

шестнадцатеричное значение без знака представляет 0xADFE74A52919134373 и подписаны -1375832923. Корректное значение должно быть подписано (знак бит = 1), но python преобразует его как unsigned, и мы имеем неправильное значение хеша после перевода с 64 до 32 бит.

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

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

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

Можете ли вы поделиться каким-либо контекстом о том, для чего используется эта хеш-функция и почему? – amcnabb

3

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

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

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

Try:

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

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

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

Предназначен для обеспечения защиты от отказа в обслуживании , вызванного тщательно подобранными входами, которые используют наихудший случай производительность вставки, о (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 до общих хэш-значений.

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

Например:

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

Это справедливо только для Python 3.x, но поскольку Python 3 является настоящим и будущим, и это единственный ответ, который обращается к этому, +1. –

Смежные вопросы