Historia pytania:
W Pythonie porównanie obiektów po wartości odbywa się za pomocą operatora '==', który wywołuje wewnętrznie metodę eq. Zachowanie tej metody domyślnie dziedziczy się z obiektu i porównuje tożsamość obiektów (dla większości klas).
Problem:
Gdy chcemy, aby instancje użytkowych klas były porównywane w sensowny sposób (na przykład dwa różne obiekty Person z takim samym zestawem danych byłyby uważane za równe), standardowe zachowanie nie jest wystarczające — trzeba wyraźnie określić, jak przebiega porównanie.
Rozwiązanie:
Aby zmienić zachowanie '==', należy nadpisać metodę eq w własnej klasie. Jeżeli klasa ma być haszowalna, trzeba również nadpisać hash.
Przykład kodu:
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
Kluczowe cechy:
Czy porównanie '==' jest zawsze symetryczne dla obiektów różnych typów?
Nie koniecznie — jeśli pierwszy obiekt zwróci NotImplemented, zostanie wywołane porównanie odwrotne. W przeciwnym razie może wystąpić asymetria.
class A: def __eq__(self, other): return True class B: pass print(A() == B()) # True print(B() == A()) # False
Jeśli nie zaimplementuję eq, jak będą porównywane obiekty jednej klasy użytkowej?
Będzie porównywana tożsamość (id) w pamięci, a nie wartości atrybutów.
class Foo: def __init__(self, x): self.x = x f1 = Foo(5) f2 = Foo(5) print(f1 == f2) # False
Czy można definiować eq bez hash dla obiektów, które mają być kluczami w słowniku?
Nie. Jeśli eq jest zmieniony, również hash jest pożądane do zdefiniowania, w przeciwnym razie obiekt stanie się niehaszowalny (TypeError podczas użycia jako klucz).
class Foo: def __eq__(self, other): return True # hash dziedziczy się z obiektu, ale zachowanie będzie nieprzewidywalne # Lepiej jawnie zdefiniować __hash__
Negatywny przypadek
Podczas projektowania klasy ValueObject w zespole nie wzięto pod uwagę, że zaimplementowano tylko eq, ale nie hash. Przy próbie użycia obiektu jako klucza w słowniku lub elementu zbioru, występowało wyjątek TypeError.
Zalety:
Szybkie wdrożenie użytkowego porównania.
Wady:
Niemożność używania obiektów w zbiorach i słownikach, trudności w debugowaniu.
Pozytywny przypadek
W innym projekcie programiści wyraźnie zaimplementowali obie metody — eq i hash, ściśle przestrzegając zasady: jeśli dwa obiekty są równe według eq, ich hashe zgadzają się.
Zalety:
Prawidłowe działanie struktur haszowanych, spójne zachowanie, łatwość w utrzymaniu.
Wady:
Utrata elastyczności: równe pod względem wartości obiekty są nieodróżnialne w kontenerach haszowych.