ProgrammationDéveloppeur Python Intermédiaire

Qu'est-ce qui définit le fonctionnement de l'opérateur de comparaison '==' et de la méthode __eq__ en Python ? Comment peut-on modifier le comportement de la comparaison pour ses propres classes ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Contexte :

En Python, la comparaison des objets par valeur se fait via l'opérateur '==', qui appelle en interne la méthode eq. Le comportement par défaut de cette méthode est hérité de object et compare l'identité des objets (pour la plupart des classes).

Problème :

Lorsque nous souhaitons que les instances de classes personnalisées soient comparées de manière significative (par exemple, deux objets Person différents avec le même ensemble de données considérés comme égaux), le comportement par défaut est insuffisant — il est nécessaire de définir explicitement comment la comparaison se déroule.

Solution :

Pour modifier le comportement de '==', il est nécessaire de redéfinir la méthode eq dans sa propre classe. Si la classe doit être hachable, il faudra également redéfinir hash.

Exemple de code :

class Person: def __init__(self, name, age): self.name = name self.age = age def __eq__(self, other): if not isinstance(other, Person): return NotImplemented return self.name == other.name and self.age == other.age p1 = Person("Ann", 25) p2 = Person("Ann", 25) print(p1 == p2) # True

Caractéristiques clés :

  • '==' appelle eq.
  • Lors de la comparaison d'objets sans eq, leur identité (id) sera comparée.
  • Pour un fonctionnement correct avec des ensembles/dictionnaires, il faut également implémenter hash.

Questions piégées.

La comparaison '==' est-elle toujours symétrique pour des objets de types différents ?

Pas nécessairement — si le premier objet retourne NotImplemented, la comparaison inverse est appelée. Sinon, cela peut conduire à une asymétrie.

class A: def __eq__(self, other): return True class B: pass print(A() == B()) # True print(B() == A()) # False

Si on ne réalise pas eq, comment seront comparés les objets d'une même classe utilisateur ?

L'identité (id) sera comparée (identité en mémoire, pas les valeurs des attributs).

class Foo: def __init__(self, x): self.x = x f1 = Foo(5) f2 = Foo(5) print(f1 == f2) # False

Peut-on définir eq sans hash pour des objets qui doivent être des clés de dictionnaire ?

Non. Si eq est modifié, hash doit également être défini, sinon l'objet deviendra non hachable (TypeError lors de son utilisation comme clé).

class Foo: def __eq__(self, other): return True # hash hérité de object, mais le comportement sera imprévisible # Il vaut mieux définir explicitement __hash__

Erreurs courantes et anti-patterns

  • Implémentation de eq sans hash là où des clés sont nécessaires dans dict/set.
  • Erreur lors de la comparaison avec des objets d'un autre type (doit retourner NotImplemented, pas False).
  • Définition incohérente de eq et hash, entraînant un comportement incorrect dans les structures de hachage.

Cas pratiques

Cas négatif

Lors de la conception de la classe ValueObject dans l'équipe, on n'a pas tenu compte du fait qu'on a seulement implémenté eq, mais pas hash. En essayant d'utiliser l'objet comme clé dans un dictionnaire ou élément d'un ensemble, une exception TypeError s'est produite.

Avantages :

Implémentation rapide de la comparaison personnalisée.

Inconvénients :

Inutilisabilité des objets dans des ensembles et dictionnaires, difficulté de débogage.

Cas positif

Dans un autre projet, les développeurs ont clairement implémenté les deux méthodes — eq et hash, suivant rigoureusement la règle : si deux objets sont égaux par eq, leurs hachages coïncident.

Avantages :

Fonctionnement correct des structures de hachage, comportement cohérent, facilité de maintenance.

Inconvénients :

Perte de flexibilité : les objets égaux par valeur sont indiscernables dans les conteneurs de hachage.