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)?
python
google-app-engine
hash
Денис Т.
источник
источник
Ответы:
Используйте hashlib, поскольку он
hash()
был разработан для :и поэтому не гарантирует, что он будет одинаковым во всех реализациях Python.
источник
hashlib
немного медленно для некриптографического использования?hash
95 ns,binascii.crc32
570 ns,hashlib.md5.digest()
1.42 us,murmur.string_hash
234 nshash
использует новое случайно сгенерированное значение соли с каждым сеансом Python. Таким образом, он будет меняться между сеансами Python.Как указано в документации, встроенная функция hash () не предназначена для хранения результирующих хэшей где-то извне. Он используется для предоставления хеш-значения объекта, для хранения его в словарях и так далее. Это также зависит от реализации (GAE использует модифицированную версию Python). Проверять, выписываться:
>>> class Foo: ... pass ... >>> a = Foo() >>> b = Foo() >>> hash(a), hash(b) (-1210747828, -1210747892)
Как видите, они разные, поскольку hash () использует
__hash__
метод объекта вместо «обычных» алгоритмов хеширования, таких как SHA.Учитывая вышеизложенное, рациональным выбором является использование модуля hashlib .
источник
int(hashlib.md5(repr(self)).hexdigest(), 16)
(при условии, чтоself.__repr__
он был определен как идентичный, если объекты идентичны). Если 32 байта слишком длинные, вы, конечно, можете уменьшить размер, разрезав шестнадцатеричную строку перед преобразованием.__repr__
он достаточно уникален, вы можете просто использоватьstr.__hash__
(т.е.hash(repr(self))
), поскольку dicts не смешивают неравные объекты с одним и тем же хешем. Это работает, только если объект достаточно тривиален, чтобы repr мог представлять личность, очевидно.a
иb
как я могу использовать модуль hashlib, чтобы убедиться, что объекты идентичны?__hash__()
и__eq__()
методы в вашем классе .Ответ совершенно не удивителен: на самом деле
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)
:Даже запуск на одной машине даст разные результаты при вызовах:
$ 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
:Например:
$ 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
источник
А как насчет бит знака?
Например:
Значение Hex
0xADFE74A5
представляет собой беззнаковый2919134373
и подписанный-1375832923
. Текущее значение должно быть подписано (бит знака = 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 или напишите свою собственную хеш-функцию.
источник