프로그래밍백엔드 개발자

파이썬에서 객체 비교 프로토콜(__eq__, __ne__, __lt__, __le__, __gt__, __ge__)는 어떻게 작동하나요? 이러한 모든 메서드가 필요한 이유와 잘못 구현했을 때의 결과는 무엇인가요?

Hintsage AI 어시스턴트로 면접 통과

답변.

질문의 역사

파이썬 초기 버전부터 객체 간 비교가 가능했습니다. 객체에 대해 "크다" 또는 "같다"는 의미를 설명하기 위해 특별한 비교 메서드(eq, ne, lt, le, gt, ge)가 만들어졌습니다. PEP 207(Python 2.1+)의 출현으로 프로그래머가 비교 동작을 명시적으로 제어할 수 있도록 단일 비교 프로토콜이 구현되었습니다.

문제

기본 타입(int, str)은 "값에 따라" 비교되지만, 사용자 정의 클래스는 기본적으로 "아이덴티티에 따라" 비교됩니다(is). 마법의 비교 메서드를 구현하지 않거나 잘못 구현하면, 객체 비교는 컬렉션, 정렬, set 및 dict 사용 시 예상치 못한 동작을 하거나 때때로 타입 오류로 이어집니다.

해결책

클래스에서 비교 메서드를 명시적으로 정의해야 합니다. 가장 자주 구현하는 것은 eq(같음)와 lt(작음)이며, 나머지 메서드는 functools.total_ordering 데코레이터를 사용하여 자동으로 얻을 수 있습니다.

코드 예시:

from functools import total_ordering @total_ordering class Point: def __init__(self, x, y): self.x = x self.y = y def __eq__(self, other): if not isinstance(other, Point): return NotImplemented return self.x == other.x and self.y == other.y def __lt__(self, other): if not isinstance(other, Point): return NotImplemented return (self.x, self.y) < (other.x, other.y) # 사용 예: p1 = Point(1, 2) p2 = Point(1, 3) print(p1 < p2) # True print(p1 == p2) # False

주요 특징:

  • 알려지지 않은 타입과 비교할 때 NotImplemented를 반환해야 하며, TypeError를 반환하지 않아야 함
  • 하나의 메서드(eq)의 잘못된 구현이 __hash__와 일치하지 않으면 집합/사전에서 작동할 때 오류가 발생함
  • functools.total_ordering은 전체 비교 세트의 구현을 단순화함

트릭 질문들.

단 하나의 비교 메서드(eq 또는 lt)만 구현하면 모든 것이 작동하나요?

아니요. __eq__만 구현하면 동등성 비교는 올바르게 동작하지만, <, >, <=, >= 연산자는 기본적으로 객체 아이디를 비교하여 동작합니다. 전체 동작을 위해서는 충분한 메서드 세트를 구현하거나 functools.total_ordering을 사용해야 합니다.

왜 compare(x, y)가 다른 언어처럼 파이썬에 존재하지 않나요?

파이썬은 각 연산자에 대한 특별한 "마법" 메서드 개념을 상속받았고, cmp 함수는 Python 3부터 제거되었습니다. 객체의 일반 비교에는 풍부한 비교 메서드(lt, eq 등)를 사용하고, 정렬에는 sort/sorted 함수의 key 매개변수를 사용합니다.

지원하는 메서드가 없는 타입과 객체를 비교하면 어떻게 되나요?

eq 또는 다른 메서드에서 지원하지 않는 타입에 대해 NotImplemented를 반환하면, 파이썬은 자동으로 두 번째 피연산자의 반대 메서드를 시도하거나 False를 반환합니다. 이러한 경우를 처리하지 않으면 TypeError가 발생하거나 전혀 잘못된 동작이 발생할 수 있습니다.

코드 예시:

class Foo: def __eq__(self, other): return NotImplemented class Bar: pass print(Foo() == Bar()) # False (Bar.__eq__를 통한 시도)

일반적인 오류 및 안티 패턴

  • __eq__를 재정의할 때 hash 구현을 잊어버리는 경우 — 객체를 사전 키로 사용할 수 없음
  • 유형 검증 없이 모든 유형에 대해 True/False 반환 — 원치 않는 일치를 초래함
  • 대칭성 위반: 예를 들어, x == y는 True지만 y == x는 False

실제 사례

부정적인 사례

개발자가 User 클래스를 만들고 hash 없이 __eq__만 구현하여 사용자가 이메일로 동등하게 판별되도록 했습니다. 이후 User 객체를 set에 사용하려고 하거나 사전의 키로 사용하려 했습니다.

장점:

  • 동등성 검증 로직이 직접 비교 시 작동함

단점:

  • 객체를 set에 추가할 수 없고, TypeError가 발생함: unhashable type: 'User'
  • ==/!= 비교만 작동하며, 집합이나 사전은 항상 객체를 다르게 평가함

긍정적인 사례

프로그래머는 @total_ordering을 사용하고 eq 및 __lt__를 타입 검사를 추가하여 구현하며, 클래스가 변경 불가능할 경우 __hash__를 추가합니다. 객체가 올바르게 비교되고 정렬되며 사전 키로 작동합니다.

장점:

  • 간단한 코드, 중복이 적음
  • 모든 컬렉션과 정렬에서 안정적인 동작

단점:

  • @total_ordering은 중요 클래스의 모든 메서드를 명시적으로 구현하는 것보다 조금 느릴 수 있음