84 votes

Fonction Python hash() intégrée

Windows XP, Python 2.5 :

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

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

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

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

91voto

Mike Hordecki Points 14757

Comme indiqué dans la documentation, la fonction intégrée hash() est pas conçu pour stocker les hachages résultants quelque part à l'extérieur. Il est utilisé pour fournir la valeur de hachage d'un objet, pour la stocker dans un dictionnaire, etc. Il est également spécifique à l'implémentation (GAE utilise une version modifiée de Python). A consulter :

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

Comme vous pouvez le constater, elles sont différentes, car hash() utilise la propriété de l'objet __hash__ au lieu des algorithmes de hachage "normaux", tels que SHA.

Compte tenu de ce qui précède, le choix rationnel est d'utiliser l'option hashlib module.

59voto

SilentGhost Points 79627

Utilisation hashlib como hash() a été conçu pour être utilisé pour :

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

et ne garantit donc pas qu'il sera le même pour toutes les implémentations de Python.

32voto

rewritten Points 7430

La réponse n'est absolument pas surprenante : en fait

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

Ainsi, si vous souhaitez obtenir des réponses fiables sur les chaînes ASCII Il suffit d'obtenir les 32 bits inférieurs sous la forme uint . La fonction de hachage pour les chaînes de caractères est sûre sur 32 bits et presque portable.

D'autre part, il ne faut pas compter sur l'obtention de la hash() de tout objet sur lequel vous n'avez pas explicitement défini la fonction __hash__ pour être invariante.

Pour les chaînes ASCII, cela fonctionne simplement parce que le hachage est calculé sur les seuls caractères formant la chaîne, comme dans le cas suivant :

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ù les c_mul est la multiplication "cyclique" (sans débordement) comme en C.

20voto

arekolek Points 1

La plupart des réponses suggèrent que cela est dû aux différentes plateformes, mais ce n'est pas tout. De la documentation de object.__hash__(self) :

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

Il s'agit d'assurer une protection contre un déni de service. causé par des entrées soigneusement choisies qui exploitent les pires performances d'une insertion de dict d'une insertion de dict, d'une complexité de O(n²). [ ] 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 de la fonction dicts , sets et d'autres correspondances. Python n'a jamais garanti cet ordre (et il varie généralement entre les versions 32 bits et 64 bits).

Même sur la même machine, les résultats varieront d'une invocation à l'autre :

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

Tandis que :

$ 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 si elle est définie à random une valeur aléatoire est utilisée pour ensemencer les hachages de str , bytes y datetime objets.

Si PYTHONHASHSEED est fixé à une valeur entière fixe pour générer le hash() des types couverts par le hachage la randomisation.

Son but est de permettre un hachage répétable, comme pour les autotests de l'interpréteur lui-même, ou pour permettre à une grappe de processus python de de partager des valeurs de hachage.

L'entier doit être un nombre décimal compris dans l'intervalle [0, 4294967295] . 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

7voto

Tzury Bar Yochay Points 3437

Les résultats du hachage varient entre les plates-formes 32bit et 64bit.

Si un hachage calculé doit être le même sur les deux plates-formes, il faut envisager d'utiliser

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X