Python >> Tutoriel Python >  >> Python

Fonction Python hash()

hash(object) intégré de Python La fonction prend un objet comme argument et renvoie sa valeur de hachage. Comme la valeur de hachage est calculée sur la base des données de l'objet, deux objets différents mais égaux doivent avoir la même valeur de hachage. Il ne s'ensuit pas, cependant, que deux objets avec la même valeur de hachage sont égaux :ils peuvent avoir la même valeur de hachage et être toujours différents.

Argumentation object Un objet pour lequel la valeur de hachage doit être calculée.
Valeur de retour int Renvoie la valeur de hachage calculée.

Apprendre Python hash() par l'exemple

L'exemple de base montre que la valeur de hachage d'un entier est l'entier lui-même :

>>> hash(42)
42

Cependant, la valeur de hachage d'une chaîne est complètement imprévisible, du moins pour vous sans connaître l'implémentation sous-jacente du hash() fonction :

>>> hash('42')
-7286207279771019371

Les valeurs de hachage des flottants sont soit l'entier converti où cette conversion ne perd aucune information, soit un entier aléatoire où le flottant a une valeur fractionnaire non nulle :

>>> hash(42.0)
42
>>> hash(42.1)
230584300921372714

Vous pouvez également calculer les valeurs de hachage des tuples et autres immuables types de collecte. Le résultat hash() la valeur est une combinaison de hash() valeurs à l'intérieur de la collection immuable.

>>> hash((1, 2, 3))
2528502973977326415

Cependant, vous ne pouvez pas calculer le hash() valeur des types de collection modifiables tels que les listes, les ensembles et les dictionnaires, car cela signifierait que chaque modification de la collection devrait modifier la valeur de hachage. Ceci est impossible car les valeurs de hachage doivent être statiques pour le même objet. C'est pourquoi Python génère une erreur si vous essayez de calculer la valeur de hachage d'un type de collection mutable.

Liste non hachable :

>>> hash([1, 2, 3])
Traceback (most recent call last):
  File "<pyshell#11>", line 1, in <module>
    hash([1, 2, 3])
TypeError: unhashable type: 'list'

Ensemble non hachable :

>>> hash({1, 2, 3})
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    hash({1, 2, 3})
TypeError: unhashable type: 'set'

Dict non hachable :

>>> hash({'Alice': 18, 'Bob': 19})
Traceback (most recent call last):
  File "<pyshell#14>", line 1, in <module>
    hash({'Alice': 18, 'Bob': 19})
TypeError: unhashable type: 'dict'

Vidéo Python hash()

Mais avant de continuer, je suis ravi de vous présenter mon nouveau livre Python Python One-Liners (Lien Amazon).

Si vous aimez les one-liners, vous allez adorer le livre. Il vous apprendra tout ce qu'il y a à savoir sur une seule ligne de code Python. Mais c'est aussi une introduction à l'informatique , science des données, apprentissage automatique et algorithmes. L'univers en une seule ligne de Python !

Le livre est sorti en 2020 avec l'éditeur de livres de programmation de classe mondiale NoStarch Press (San Francisco).

Lien :https://nostarch.com/pythononeliners

Mise en œuvre de Python hash() pour les objets personnalisés

Et si vous définissiez votre propre classe - comment modifier le calcul du hash() valeur ?

Par exemple, supposons que vous créez une classe Client qui a une valeur, la valeur du client pour votre entreprise :

class Customer:
    def __init__(self, value):
        self.value = value


alice = Customer(1000)
bob = Customer(1000)

print('Alice Hash Value:', hash(alice))
print('Bob Hash Value:', hash(bob))

La sortie peut être les deux valeurs de hachage suivantes :

Alice Hash Value: -9223371944682613630
Bob Hash Value: 92172188494

Le problème avec cela est que les deux doivent avoir la même valeur de hachage car ils doivent être considérés comme égaux. Alors, comment pouvez-vous modifier la sortie du hash() fonction sur un objet personnalisé ?

En interne, Python appelle le object.__hash__() méthode dunder pour calculer le hash(object) évaluer. La seule chose à faire est d'écraser son comportement. Vous pouvez calculer la valeur de hachage de l'objet personnalisé comme une combinaison de la valeur de hachage de ses attributs en utilisant hash() appels de fonction sur ceux-ci !


class Customer:
    def __init__(self, value):
        self.value = value

    def __hash__(self):
        return hash(self.value)

alice = Customer(1000)
bob = Customer(1000)
print('Alice Hash Value:', hash(alice))
print('Bob Hash Value:', hash(bob))

La valeur de hachage d'un objet Client est désormais la valeur de hachage de l'attribut entier associé value :

Alice Hash Value: 1000
Bob Hash Value: 1000

Les deux objets ont maintenant la même valeur de hachage !

Différents objets peuvent-ils avoir la même valeur de hachage ?

Comme la valeur de hachage est calculée sur la base des données de l'objet, deux objets différents mais égaux doivent avoir la même valeur de hachage. Dans l'exemple suivant, nous créons deux objets tuple avec le même contenu. Mais ce sont quand même des objets différents comme le prouve l'appel t1 is t2 ce qui donne False .

>>> t1 = (1, 2)
>>> t2 = (1, 2)
>>> t1 is t2
False

Cependant, le hash() la valeur des deux est la même !

>>> hash(t1)
3713081631934410656
>>> hash(t2)
3713081631934410656
>>> hash(t1) == hash(t2)
True

Il ne s'ensuit pas, cependant, que deux objets avec la même valeur de hachage sont égaux :ils peuvent avoir la même valeur de hachage et être toujours différents.

Relation __eq__() et __hash__()

Notez que c'est une bonne pratique et évite de nombreux problèmes ultérieurs d'implémenter également le __eq__() méthode lors de l'écrasement de __hash__() . Sinon, deux objets avec la même valeur de hachage peuvent toujours être considérés comme différents lors de l'utilisation du == comparaison. Ce serait illogique car la même valeur de hachage indique qu'ils sont considérés comme égaux !

# BAD PRACTICE 
# (no __eq__ method)
class Customer:
    def __init__(self, value):
        self.value = value

    def __hash__(self):
        return hash(self.value)


alice = Customer(1000)
bob = Customer(1000)
print('Alice Hash Value:', hash(alice))
print('Bob Hash Value:', hash(bob))
print('Are they equal?', bob == alice)

La sortie incohérente indique que bien qu'Alice et Bob aient la même valeur de hachage, ils sont toujours considérés comme inégaux.

Alice Hash Value: 1000
Bob Hash Value: 1000
Are they equal? False

Au lieu de cela, l'exemple correct serait le suivant où le __eq__ méthode est écrasée.

# GOOD PRACTICE 
# (defined __eq__ method)
class Customer:
    def __init__(self, value):
        self.value = value

    def __hash__(self):
        return hash(self.value)

    def __eq__(self, other):
        return self.value == other.value

alice = Customer(1000)
bob = Customer(1000)
print('Alice Hash Value:', hash(alice))
print('Bob Hash Value:', hash(bob))
print('Are they equal?', bob == alice)

Maintenant, la sortie est plus cohérente :

Alice Hash Value: 1000
Bob Hash Value: 1000
Are they equal? True

Résumé

hash(object) intégré de Python La fonction prend un objet comme argument et renvoie sa valeur de hachage.

>>> hash(42)
42

Comme la valeur de hachage est calculée sur la base des données de l'objet, deux objets différents mais égaux doivent avoir la même valeur de hachage.

>>> t1 = (1, 2)
>>> t2 = (1, 2)
>>> hash(t1)
3713081631934410656
>>> hash(t2)
3713081631934410656
>>> hash(t1) == hash(t2)
True
>>> t1 is t2
False

Il ne s'ensuit pas, cependant, que deux objets avec la même valeur de hachage sont égaux :ils peuvent avoir la même valeur de hachage et être toujours différents.