문제의 역사:
Python에서 객체를 값으로 비교할 때 연산자 '=='가 사용되며, 이는 내부적으로 eq 메소드를 호출합니다. 이 메소드의 기본 동작은 object에서 상속되며, 객체의 동일성을 비교합니다(대부분의 클래스에 대해).
문제:
사용자 정의 클래스의 인스턴스가 의미 있게 비교되기를 원할 때(예를 들어, 동일한 데이터 세트를 가진 두 개의 서로 다른 Person 객체가 같다고 간주될 경우) 기본 동작은 불충분합니다 — 비교 방법을 명시적으로 정의해야 합니다.
해결책:
'=='의 동작을 변경하려면 자신의 클래스에서 eq 메소드를 재정의해야 합니다. 클래스가 해시 가능한 경우, __hash__도 재정의해야 합니다.
코드 예:
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
핵심 사항:
서로 다른 타입의 객체에 대해 '==' 비교가 항상 대칭적입니까?
반드시 그렇지는 않습니다 — 첫 번째 객체가 NotImplemented를 반환하면, 반대 비교가 호출됩니다. 그렇지 않으면 비대칭이 발생할 수 있습니다.
class A: def __eq__(self, other): return True class B: pass print(A() == B()) # True print(B() == A()) # False
__eq__를 구현하지 않으면, 같은 사용자 정의 클래스의 객체들은 어떻게 비교됩니까?
id(메모리에서의 동일성, 속성 값이 아닌)를 비교합니다.
class Foo: def __init__(self, x): self.x = x f1 = Foo(5) f2 = Foo(5) print(f1 == f2) # False
딕셔너리의 키로 사용해야 하는 객체에 대해 hash 없이 __eq__를 정의할 수 있습니까?
아니요. __eq__가 변경된 경우, __hash__도 정의하는 것이 바람직합니다. 그렇지 않으면 객체가 해시 가능하지 않게 되어 TypeError가 발생합니다.
class Foo: def __eq__(self, other): return True # 해시는 object에서 상속되지만, 동작이 예측할 수 없게 됩니다. # __hash__를 명시적으로 정의하는 것이 좋습니다.
부정적인 사례
ValueObject 클래스를 설계할 때 __eq__만 구현하고 __hash__를 고려하지 못하여, 이 객체를 딕셔너리의 키 또는 집합의 요소로 사용할 때 TypeError 예외가 발생했습니다.
장점:
사용자 비교의 빠른 도입.
단점:
집합과 딕셔너리에서 객체를 사용할 수 없으며, 디버깅이 어렵습니다.
긍정적인 사례
다른 프로젝트에서 개발자들은 두 메소드(__eq__와 hash)를 명시적으로 구현하고, 두 객체가 __eq__에 의해 같으면 그들의 해시도 같아야 한다는 규칙을 엄격히 따랐습니다.
장점:
해시 구조가 올바로 작동하며, 일관된 동작과 유지 관리의 용이함.
단점:
값이 같은 객체가 해시 컨테이너에서 구별되지 않기 때문에 유연성이 감소합니다.