Windows XP, Python 2.5:
hash('http://stackoverflow.com') Result: 1934711907
Google App Engine ( http://shell.appspot.com/ ):
hash('http://stackoverflow.com') Result: -5768830964305142685
¿Porqué es eso? ¿Cómo puedo tener una función hash que me dé los mismos resultados en diferentes plataformas (Windows, Linux, Mac)?
python
google-app-engine
hash
Denis T.
fuente
fuente
Respuestas:
Use hashlib como
hash()
fue diseñado para usarse para :y por lo tanto no garantiza que será igual en todas las implementaciones de Python.
fuente
hashlib
un poco lentas para uso no criptográfico?hash
95 ns,binascii.crc32
570 ns,hashlib.md5.digest()
1.42 us,murmur.string_hash
234 nshash
usa un nuevo valor de sal generado aleatoriamente con cada sesión de Python. Entonces cambiará entre sesiones de Python.Como se indica en la documentación, la función hash () incorporada no está diseñada para almacenar los hash resultantes en algún lugar externo. Se utiliza para proporcionar el valor hash de los objetos, almacenarlos en diccionarios, etc. También es específico de la implementación (GAE usa una versión modificada de Python). Revisa:
>>> class Foo: ... pass ... >>> a = Foo() >>> b = Foo() >>> hash(a), hash(b) (-1210747828, -1210747892)
Como puede ver, son diferentes, ya que hash () usa el
__hash__
método del objeto en lugar de los algoritmos de hash "normales", como SHA.Dado lo anterior, la elección racional es utilizar el módulo hashlib .
fuente
int(hashlib.md5(repr(self)).hexdigest(), 16)
(asumiendo queself.__repr__
se ha definido como idéntico si los objetos son idénticos). Si 32 bytes son demasiado largos, por supuesto, puede reducir el tamaño cortando la cadena hexadecimal antes de la conversión.__repr__
es lo suficientemente único, podría usarstr.__hash__
(es decirhash(repr(self))
), ya que los dictados no mezclan objetos no iguales con el mismo hash. Esto solo funciona si el objeto es lo suficientemente trivial como para que la repr pueda representar la identidad, obviamente.a
yb
, ¿cómo podría usar el módulo hashlib para ver que los objetos son idénticos?__hash__()
y__eq__()
en su clase .La respuesta no es ninguna sorpresa: de hecho
In [1]: -5768830964305142685L & 0xffffffff Out[1]: 1934711907L
así que si desea obtener respuestas confiables en cadenas ASCII , simplemente obtenga los 32 bits inferiores como
uint
. La función hash para cadenas es segura para 32 bits y casi portátil.Por otro lado, no puede confiar en absoluto en obtener el valor
hash()
de cualquier objeto sobre el que no haya definido explícitamente que el__hash__
método sea invariante.Sobre cadenas ASCII, funciona solo porque el hash se calcula en los caracteres individuales que forman la cadena, como el siguiente:
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
donde la
c_mul
función es la multiplicación "cíclica" (sin desbordamiento) como en C.fuente
La mayoría de las respuestas sugieren que esto se debe a las diferentes plataformas, pero hay más. De la documentación de
object.__hash__(self)
:Incluso la ejecución en la misma máquina producirá resultados variables en todas las invocaciones:
$ python -c "print(hash('http://stackoverflow.com'))" -3455286212422042986 $ python -c "print(hash('http://stackoverflow.com'))" -6940441840934557333
Mientras:
$ python -c "print(hash((1,2,3)))" 2528502973977326415 $ python -c "print(hash((1,2,3)))" 2528502973977326415
Consulte también la variable de entorno
PYTHONHASHSEED
:Por ejemplo:
$ export PYTHONHASHSEED=0 $ python -c "print(hash('http://stackoverflow.com'))" -5843046192888932305 $ python -c "print(hash('http://stackoverflow.com'))" -5843046192888932305
fuente
Los resultados de hash varían entre plataformas de 32 bits y 64 bits
Si un hash calculado debe ser el mismo en ambas plataformas, considere usar
def hash32(value): return hash(value) & 0xffffffff
fuente
Supongo que AppEngine está utilizando una implementación de Python de 64 bits (-5768830964305142685 no cabe en 32 bits) y su implementación de Python es de 32 bits. No puede confiar en que los hash de objetos sean comparables de manera significativa entre diferentes implementaciones.
fuente
Esta es la función hash que Google usa en producción para 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
fuente
¿Qué pasa con el bit de señal?
Por ejemplo:
El valor hexadecimal
0xADFE74A5
representa sin firmar2919134373
y firmado-1375832923
. El valor actual debe estar firmado (bit de signo = 1) pero Python lo convierte como sin firmar y tenemos un valor hash incorrecto después de la traducción de 64 a 32 bits.Tenga cuidado al usar:
def hash32(value): return hash(value) & 0xffffffff
fuente
Hash polinomial para cadenas.
1000000009
y239
son números primos arbitrarios. Es poco probable que se produzcan colisiones por accidente. La aritmética modular no es muy rápida, pero para prevenir colisiones es más confiable que tomarla como módulo2
. Por supuesto, es fácil encontrar una colisión a propósito.mod=1000000009 def hash(s): result=0 for c in s: result = (result * 239 + ord(c)) % mod return result % mod
fuente
El valor de PYTHONHASHSEED podría usarse para inicializar los valores hash.
Tratar:
PYTHONHASHSEED python -c 'print(hash('http://stackoverflow.com'))'
fuente
Probablemente solo pregunte la función proporcionada por el sistema operativo, en lugar de su propio algoritmo.
Como dicen otros comentarios, use hashlib o escriba su propia función hash.
fuente