2009-04-27 6 views
80

Windows XP, Python 2.5:Construit en fonction de hachage python()

hash('http://stackoverflow.com') Result: 1934711907 

Google App Engine (http://shell.appspot.com/):

hash('http://stackoverflow.com') Result: -5768830964305142685 

Pourquoi est-ce? Comment puis-je avoir une fonction de hachage qui me donnera les mêmes résultats sur différentes plateformes (Windows, Linux, Mac)?

+14

cela doit à fait votre winxp est une plate-forme 32 bits tandis que Google est 64 bits –

Répondre

54

Utilisez hashlib comme hash()was designed to be used to:

comparer rapidement les clés de dictionnaire lors d'une recherche dans le dictionnaire

et ne garantit donc pas que ce sera la même pour les implémentations de Python.

+5

ne sont pas les fonctions de hachage dans 'hashlib' un peu lent pour non-cryptographique utilisation? –

+0

@Brandon: sont-ils? repères? patches? – SilentGhost

+8

Ils sont en fait très lents par rapport aux fonctions de hachage à usage général tels que Jenkins, Bernstein, FNV, MurmurHash, et bien d'autres. Si vous cherchez à créer votre propre structure de table de hachage, je suggère de regarder uthash.h http://uthash.sourceforge.net/ – lericson

-3

Il demande probablement simplement la fonction fournie par le système d'exploitation, plutôt que son propre algorithme.

Comme d'autres commentaires dit, utilisez hashlib ou écrivez votre propre fonction de hachage.

88

Comme indiqué dans la documentation, la fonction hash() intégrée est et non conçue pour stocker les hachages résultants quelque part en externe. Il est utilisé pour fournir la valeur de hachage de l'objet, pour les stocker dans les dictionnaires et ainsi de suite. Il est également spécifique à l'implémentation (GAE utilise une version modifiée de Python). Départ:

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

Comme vous pouvez le voir, ils sont différents, comme hachage() utilise la méthode de l'objet __hash__ au lieu des algorithmes de hachage « normaux », tels que SHA. Étant donné ce qui précède, le choix rationnel est d'utiliser le module hashlib.

+0

Merci! Je suis venu ici me demandant pourquoi j'obtiendrais toujours des valeurs de hachage différentes pour des objets identiques résultant en un comportement inattendu avec des dicts (qui indexent par hachage + type plutôt que de vérifier l'égalité). Un moyen rapide de générer votre propre hash int à partir de hashlib.md5 est 'int (hashlib.md5 (repr (self)) .hexdigest(), 16)' (en supposant que 'self .__ repr__' a été défini comme étant identique aux objets iff sont identiques). Si 32 octets sont trop longs, vous pouvez bien sûr couper la taille en coupant la chaîne hexagonale avant la conversion. –

+1

À la réflexion, si '__repr__' est suffisamment unique, vous pouvez simplement utiliser' str .__ hash__' (c'est-à-dire 'hash (repr (self))') car les dicts ne mélangent pas les objets non égaux avec le même hash. Cela ne fonctionne que si l'objet est assez trivial pour que le repr puisse représenter l'identité, évidemment. –

+0

Donc, dans votre exemple avec deux objets 'a' et' b', comment pourrais-je utiliser le module hashlib pour voir que les objets sont identiques? – Garrett

6

En un mot, AppEngine utilise une implémentation 64 bits de Python (-5768830964305142685 ne rentrera pas dans 32 bits) et votre implémentation de Python est de 32 bits. Vous ne pouvez pas compter sur des hachages d'objet étant significativement comparables entre différentes implémentations.

32

La réponse est absolument pas surprenant: en effet

In [1]: -5768830964305142685L & 0xffffffff 
Out[1]: 1934711907L 

donc si vous voulez obtenir des réponses fiables sur les chaînes ASCII, juste obtenir les 32 bits inférieurs comme uint. La fonction de hachage pour les chaînes est sûre de 32 bits et presque portable. D'autre part, vous ne pouvez pas compter sur hash() pour qu'aucun objet sur lequel vous n'avez pas explicitement défini la méthode __hash__ ne soit invariable.

Plus de chaînes ASCII, il fonctionne simplement parce que le hachage est calculée sur les caractères individuels formant la chaîne, comme ce qui suit:

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 

où la fonction c_mul est la multiplication « cyclique » (sans trop-plein) comme dans C.

8

résultats Hash entre les plates-formes varie 32bits et 64bits

Si un hachage calculée doit être la même sur les deux plates-formes envisager d'utiliser

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

Qu'en est-bit de signe?

Par exemple:

valeur hexadécimale non signée 0xADFE74A5 représente 2919134373 et signé -1375832923. La valeur Currect doit être signée (bit de signe = 1) mais python la convertit comme non signée et nous avons une valeur de hachage incorrecte après traduction de 64 à 32 bits.

Soyez prudent en utilisant:

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

Ceci est la fonction de hachage que Google utilise dans la production pour 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

Pouvez-vous partager un contexte sur l'utilisation de cette fonction de hachage et pourquoi? – amcnabb

3

hachage polynomiale pour les chaînes. 1000000009 et 239 sont des nombres premiers arbitraires. Peu probable d'avoir des collisions par accident. L'arithmétique modulaire n'est pas très rapide, mais pour prévenir les collisions c'est plus fiable que de prendre modulo une puissance de 2. Bien sûr, il est facile de trouver une collision intentionnellement.

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

La valeur de PYTHONHASHSEED peut être utilisée pour initialiser les valeurs de hachage.

Essayez:

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

La plupart des réponses suggèrent que cela est dû au fait des différentes plates-formes, mais il y a plus à elle. De the documentation of object.__hash__(self):

Par défaut, les valeurs de __hash__()str, bytes et datetime objets sont « salés » avec une valeur aléatoire imprévisible. Bien qu'ils restent constants dans un processus Python individuel, ils ne sont pas prévisibles entre les invocations répétées de Python.

Ceci est destiné à fournir une protection contre un déni de service causé par des entrées soigneusement choisies qui exploitent le pire des cas performances d'une insertion dict, O (n²) complexité. Voir http://www.ocert.org/advisories/ocert-2011-003.html pour plus de détails.

La modification des valeurs de hachage affecte l'ordre d'itération dicts, sets et d'autres mappages. Python n'a jamais fait de garanties à propos de cette commande (et il varie généralement entre les versions 32 bits et 64 bits).

Même en cours d'exécution sur la même machine donnera des résultats différents à travers les invocations:

$ python -c "print(hash('http://stackoverflow.com'))" 
-3455286212422042986 
$ python -c "print(hash('http://stackoverflow.com'))" 
-6940441840934557333 

Alors:

$ python -c "print(hash((1,2,3)))" 
2528502973977326415 
$ python -c "print(hash((1,2,3)))" 
2528502973977326415 

Voir aussi la variable d'environnement PYTHONHASHSEED:

Si cette variable n'est pas définie ou définie sur random, une valeur aléatoire est utilisée pour graver les hachages des objets str, bytes et datetime.

Si PYTHONHASHSEED est réglé sur une valeur entière, il est utilisé comme une graine fixe pour générer le hash() des types couverts par la randomisation de hachage .

Son but est de permettre le hachage reproductible, comme pour autotests pour l'interprète lui-même, ou pour permettre à un groupe de processus de python pour partager des valeurs de hachage. Le nombre entier doit être un nombre décimal compris entre [0, 4294967295] et

. La spécification de la valeur 0 désactive la randomisation du hachage.

Par exemple:

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

Cela n'est vrai que pour Python 3.x, mais puisque Python 3 est le présent et le futur et que c'est la seule réponse qui aborde cela, +1. –

Questions connexes