Comparaison d'objets en Python – Real Python

By | janvier 29, 2020

Expert Python

Il existe une différence subtile entre l'opérateur d'identité Python (est) et l'opérateur d'égalité (==). Votre code peut fonctionner correctement lorsque vous utilisez Python est pour comparer les nombres, jusqu'à ce que ce ne soit pas le cas. Vous avez peut-être entendu quelque part que le Python est l'opérateur est plus rapide que le == opérateur, ou vous pouvez penser qu'il semble plus Pythonic. Cependant, il est essentiel de garder à l’esprit que ces opérateurs ne se comportent pas tout à fait de la même manière.

le == l'opérateur compare la valeur ou égalité de deux objets, alors que le Python est L'opérateur vérifie si deux variables pointent vers le même objet en mémoire. Dans la grande majorité des cas, cela signifie que vous devez utiliser les opérateurs d'égalité == et ! =, sauf lorsque vous comparez à Aucun.

Dans ce didacticiel, vous apprendrez:

  • Quelle est la différence entre égalité et identité des objets
  • Quand utiliser les opérateurs d'égalité et d'identité pour comparer des objets
  • Quoi d'autre Opérateurs Python faire sous le capot
  • Pourquoi utiliser est et n'est pas comparer les valeurs conduit à comportement inattendu
  • Comment écrire un Douane __eq __ () méthode de classe définir le comportement de l'opérateur d'égalité

La comparaison de l'identité avec Python est et n'est pas des opérateurs

Le Python est et n'est pas les opérateurs comparent les identité de deux objets. En CPython, c'est leur adresse mémoire. Tout en Python est un objet, et chaque objet est stocké à un emplacement mémoire spécifique. Le Python est et n'est pas les opérateurs vérifient si deux variables font référence au même objet en mémoire.

Vous pouvez utiliser id () pour vérifier l'identité d'un objet:

>>>

>>> Aidez-moi(id)
Aide sur l'ID de fonction intégrée dans les modules intégrés:

id (obj, /)
                Renvoie l'identité d'un objet.

                Ceci est garanti unique parmi les objets existants simultanément.
                (CPython utilise l'adresse mémoire de l'objet.)

>>> id(id)
2570892442576

La dernière ligne montre l'adresse mémoire où la fonction intégrée id lui-même est stocké.

Il existe des cas courants où les objets avec la même valeur auront le même identifiant par défaut. Par exemple, les nombres -5 à 256 sont interné en CPython. Chaque nombre est stocké à un endroit singulier et fixe en mémoire, ce qui économise de la mémoire pour les entiers couramment utilisés.

Vous pouvez utiliser sys.intern () aux chaînes internes pour la performance. Cette fonction vous permet de comparer leurs adresses mémoire plutôt que de comparer les chaînes caractère par caractère:

>>>

>>> de sys importation interne
>>> une = 'Bonjour le monde'
>>> b = 'Bonjour le monde'
>>> une est b
Faux
>>> id(une)
1603648396784
>>> id(b)
1603648426160

>>> une = interne(une)
>>> b = interne(b)
>>> une est b
Vrai
>>> id(une)
1603648396784
>>> id(b)
1603648396784

Les variables une et b pointer initialement vers deux objets différents en mémoire, comme le montrent leurs ID différents. Lorsque vous les internez, vous vous assurez que une et b pointer vers le même objet en mémoire. Toute nouvelle chaîne avec la valeur 'Bonjour le monde' sera maintenant créé à un nouvel emplacement mémoire, mais lorsque vous internerez cette nouvelle chaîne, vous vous assurerez qu'elle pointe vers la même adresse mémoire que la première 'Bonjour le monde' que vous avez interné.

Les autres objets internés par défaut sont Aucun, Vrai, Fauxet des chaînes simples. Gardez à l'esprit que la plupart du temps, différents objets avec la même valeur seront stockés à des adresses mémoire distinctes. Cela signifie que vous ne devez pas utiliser le Python est pour comparer les valeurs.

Lorsque seuls quelques entiers sont internés

Dans les coulisses, Python interne des objets avec des valeurs couramment utilisées (par exemple, les entiers -5 à 256) pour économiser de la mémoire. Le bit de code suivant vous montre comment seuls certains entiers ont une adresse mémoire fixe:

>>>

>>> une = 256
>>> b = 256
>>> une est b
Vrai
>>> id(une)
1638894624
>>> id(b)
1638894624

>>> une = 257
>>> b = 257
>>> une est b
Faux

>>> id(une)
2570926051952
>>> id(b)
2570926051984

Initialement, une et b pointer vers le même objet interné en mémoire, mais lorsque leurs valeurs sont en dehors de la plage de entiers communs (allant de -5 à 256), ils sont stockés à des adresses de mémoire distinctes.

Lorsque plusieurs variables pointent vers le même objet

Lorsque vous utilisez l'opérateur d'affectation (=) pour rendre une variable égale à l'autre, vous faites pointer ces variables vers le même objet en mémoire. Cela peut entraîner un comportement inattendu pour les objets mutables:

>>>

>>> une = [[[[1, 2, 3]
>>> b = une
>>> une
[1, 2, 3]
>>> b
[1, 2, 3]

>>> une.ajouter(4)
>>> une
[1, 2, 3, 4]
>>> b
[1, 2, 3, 4]

>>> id(une)
2570926056520
>>> id(b)
2570926056520

Qu'est-ce qui vient de se passer? Vous ajoutez un nouvel élément à une, mais maintenant b contient également cet élément! Eh bien, dans la ligne où b = a, vous définissez b pour pointer vers la même adresse mémoire que une, de sorte que les deux variables font désormais référence au même objet.

Si vous définissez ces listes indépendamment les unes des autres, elles sont stockées à différentes adresses mémoire et se comportent indépendamment:

>>>

>>> une = [[[[1, 2, 3]
>>> b = [[[[1, 2, 3]
>>> une est b
Faux
>>> id(une)
2356388925576
>>> id(b)
2356388952648

Car une et b se réfèrent maintenant à différents objets en mémoire, changer l'un n'affecte pas l'autre.

Comparaison de l'égalité avec les opérateurs Python == et! =

Rappelez-vous que les objets avec le même valeur sont souvent stockés à adresses de mémoire distinctes. Utilisez les opérateurs d'égalité == et ! = si vous souhaitez vérifier si deux objets ont la même valeur, quel que soit leur emplacement de stockage. Dans la grande majorité des cas, c'est ce que vous voulez faire.

Lorsque la copie d'objet est égale mais pas identique

Dans l'exemple ci-dessous, vous définissez b être une copie de une (qui est un objet modifiable, tel qu'une liste ou un dictionnaire). Les deux variables auront la même valeur, mais chacune sera stockée à une adresse mémoire différente:

>>>

>>> une = [[[[1, 2, 3]
>>> b = une.copie()
>>> une
[1, 2, 3]
>>> b
[1, 2, 3]

>>> une == b
Vrai
>>> une est b
Faux

>>> id(une)
2570926058312
>>> id(b)
2570926057736

une et b sont maintenant stockés à différentes adresses mémoire, donc a est b ne reviendra plus Vrai. cependant, a == b Retour Vrai car les deux objets ont la même valeur.

Fonctionnement de la comparaison par égalité

La magie de l'opérateur d'égalité == arrive dans le __eq __ () méthode de classe de l'objet à gauche de la == signe.

Il s'agit d'une méthode de classe magique qui est appelée chaque fois qu'une instance de cette classe est comparée à un autre objet. Si cette méthode n'est pas implémentée, alors == compare les adresses mémoire des deux objets par défaut.

Comme exercice, faites un SillyString classe qui hérite de str et mettre en œuvre __eq __ () pour comparer si la longueur de cette chaîne est la même que la longueur de l'autre objet:

classe SillyString(str):
    # Cette méthode est appelée lors de l'utilisation de == sur l'objet
    def __eq__(soi, autre):
        impression(F'comparant soi    à autre")
        # Renvoie True si self et other ont la même longueur
        revenir len(soi) == len(autre)    

Maintenant, un SillyString 'Bonjour le monde' doit être égal à la chaîne «salut du monde», et même à tout autre objet de même longueur:

>>>

>>> # Comparez deux chaînes
>>> 'Bonjour le monde' == «salut du monde»
Faux

>>> # Comparez une chaîne avec une SillyString
>>> 'Bonjour le monde' == SillyString(«salut du monde»)
comparer le monde bonjour au monde bonjour
Vrai

>>> # Comparez un SillyString avec une liste
>>> SillyString('Bonjour le monde') == [[[[1, 2, 3, 4, 5, 6, sept, 8, 9, dix, 11]
comparer bonjour au monde [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
Vrai

C'est, bien sûr, un comportement stupide pour un objet qui se comporte autrement comme une chaîne, mais cela illustre ce qui se passe lorsque vous comparez deux objets à l'aide ==. le ! = l'opérateur donne la réponse inverse de cela à moins qu'un spécifique __ne __ () la méthode de classe est implémentée.

L'exemple ci-dessus vous montre également clairement pourquoi il est recommandé d'utiliser Python est opérateur pour comparer avec Aucun, à la place du == opérateur. Non seulement il est plus rapide car il compare les adresses mémoire, mais il est également plus sûr car il ne dépend de la logique d'aucun __eq __ () méthodes de classe.

Comparaison des opérateurs de comparaison Python

En règle générale, vous devez toujours utiliser les opérateurs d'égalité == et ! =, sauf lorsque vous comparez à Aucun:

  • Utilisez le Python == et ! = opérateurs pour comparer l'égalité des objets. Ici, vous comparez généralement la valeur de deux objets. Voici ce dont vous avez besoin si vous voulez comparer si deux objets ont le même contenu ou non, et vous ne vous souciez pas de savoir où ils sont stockés en mémoire.

  • Utilisez le Python est et n'est pas opérateurs lorsque vous souhaitez comparer l'identité de l'objet. Ici, vous comparez si deux variables pointent ou non vers le même objet en mémoire. Le cas d'utilisation principal de ces opérateurs est lorsque vous comparez à Aucun. Il est plus rapide et plus sûr de se comparer à Aucun par adresse mémoire que par des méthodes de classe.

Les variables ayant la même valeur sont souvent stockées à des adresses mémoire distinctes. Cela signifie que vous devez utiliser == et ! = pour comparer leurs valeurs et utiliser le Python est et n'est pas opérateurs uniquement lorsque vous souhaitez vérifier si deux variables pointent vers la même adresse mémoire.

Conclusion

Dans ce didacticiel, vous avez appris que == et ! = comparer la valeur de deux objets, alors que le Python est et n'est pas les opérateurs comparent si deux variables faire référence au même objet en mémoire. Si vous gardez cette distinction à l'esprit, vous devriez pouvoir empêcher un comportement inattendu dans votre code.

Si vous voulez en savoir plus sur le monde merveilleux de internement d'objet et le Python est , puis découvrez pourquoi vous ne devriez presque jamais utiliser «is» en Python. Vous pouvez également voir comment vous pouvez utiliser sys.intern () pour optimiser l'utilisation de la mémoire et les temps de comparaison pour les chaînes, bien qu'il soit probable que Python gère déjà automatiquement cela pour vous en arrière-plan.

Maintenant que vous avez appris ce que opérateurs d'égalité et d'identité faire sous le capot, vous pouvez essayer d'écrire votre propre __eq __ () méthodes de classe, qui définissent comment les instances de cette classe sont comparées lors de l'utilisation de la classe == opérateur. Allez et appliquez votre nouvelle connaissance de ces opérateurs de comparaison Python!