본문 바로가기

매직 메서드 - 비교 연산

1. 동등성 비교 (Equality)

__eq__(==)와 __ne__(!=) 매직메서드는 두 객체가 같은지 또는 다른지를 비교합니다.

class Book:
    def __init__(self, title, isbn):
        self.title = title
        self.isbn = isbn
    
    def __eq__(self, other):
        if not isinstance(other, Book):
            return NotImplemented
        return self.isbn == other.isbn
    
    def __ne__(self, other):
        if not isinstance(other, Book):
            return NotImplemented
        return self.isbn != other.isbn
    
    def __str__(self):
        return f"Book({self.title})"

# 같은 ISBN을 가진 두 책
book1 = Book("Python Basic", "123-456")
book2 = Book("파이썬 기초", "123-456")
print(f"같은 책?: {book1 == book2}")  # True (ISBN이 같음)

# 다른 ISBN을 가진 책
book3 = Book("Python Basic", "789-123")
print(f"다른 책?: {book1 != book3}")  # True (ISBN이 다름)

위 코드에는 동등성 비교를 할 때 주의할 만한 것을 담아두었습니다.

  1. isinstance() 체크로 타입 안전성 확보
  2. NotImplemented 반환으로 다른 타입과의 비교 허용
  3. __ne__는 __eq__의 반대로 구현

이렇게 하지 않으면 아래와 같은 오해가 생길 수 있습니다. book1이 비어있지 않은데 비어있는 것처럼 반환되는 것이죠. 따라서 타입확인과 NotImplemented 반환을 통해 이러한 오해를 방지할 수 있습니다.

class Book:
    def __eq__(self, other):
        return True

book = Book()

print(book == "Python")  # True가 되어버림 (의도하지 않은 동작)
print(book == None)      # AttributeError 발생!
class Book:
    def __init__(self, title):
        self.title = title
    
    def __eq__(self, other):
        if not isinstance(other, Book):  # 타입 체크
            return NotImplemented
        return self.title == other.title

book = Book("Python")
print(book == "Python")  # False (의도한 대로 동작)
print(book == None)      # False (안전하게 동작)

2. 순서 비교 (Ordering)

__lt__(<), __le__(<=), __gt__(>), __ge__(>=) 매직메서드로 객체 간의 순서를 정의할 수 있습니다. 여기서 순서라고 얘기한 것은 단지 크기비교로도 해당 매직메서드가 사용되지만 max, min 등의 함수를 사용할 때에도 해당 매직메서드가 사용된다는 것을 의미합니다.

class Temperature:
    def __init__(self, celsius):
        self.celsius = celsius
    
    def __lt__(self, other):
        if not isinstance(other, Temperature):
            return NotImplemented
        return self.celsius < other.celsius
    
    def __le__(self, other):
        if not isinstance(other, Temperature):
            return NotImplemented
        return self.celsius <= other.celsius
    
    def __str__(self):
        return f"{self.celsius}°C"

    def __repr__(self):
        return f"{self.celsius}°C"

# 온도 비교
temp1 = Temperature(20)
temp2 = Temperature(25)

print(f"{temp1} < {temp2}: {temp1 < temp2}")   # True
print(f"{temp1} <= {temp2}: {temp1 <= temp2}")  # True

max([temp1, temp2])  # 25°C # lt와 le를 주석처리해보세요.

3. total_ordering 데코레이터 활용

@total_ordering 데코레이터를 사용하면 __lt__와 __eq__만 구현해도 나머지 비교 연산자가 자동으로 구현됩니다.

from functools import total_ordering

@total_ordering
class Grade:
    def __init__(self, score):
        self.score = score
    
    def __eq__(self, other):
        if not isinstance(other, Grade):
            return NotImplemented
        return self.score == other.score
    
    def __lt__(self, other):
        if not isinstance(other, Grade):
            return NotImplemented
        return self.score < other.score
    
    def __str__(self):
        return f"Grade({self.score})"

# 성적 비교
math = Grade(85)
science = Grade(90)
history = Grade(85)

print(f"{math} < {science}: {math < science}")    # True
print(f"{math} == {history}: {math == history}")  # True
print(f"{math} > {science}: {math > science}")    # False (자동 구현)
print(f"{math} >= {history}: {math >= history}")  # True (자동 구현)

4. 실제 활용 예시

실제 프로그래밍에서 자주 사용되는 버전 비교 예제입니다.

class Version:
    def __init__(self, major, minor, patch):
        self.major = major
        self.minor = minor
        self.patch = patch
    
    def __eq__(self, other):
        if not isinstance(other, Version):
            return NotImplemented
        return (self.major, self.minor, self.patch) == \
               (other.major, other.minor, other.patch)
    
    def __lt__(self, other):
        if not isinstance(other, Version):
            return NotImplemented
        return (self.major, self.minor, self.patch) < \
               (other.major, other.minor, other.patch)
    
    def __str__(self):
        return f"v{self.major}.{self.minor}.{self.patch}"

# 버전 비교
v1 = Version(2, 0, 0)
v2 = Version(2, 1, 0)
v3 = Version(2, 1, 0)

print(f"{v1} < {v2}: {v1 < v2}")    # True
print(f"{v2} == {v3}: {v2 == v3}")  # True
매직 메서드 - 비교 연산 - 파이썬 클래스 톺아보기 | 위니버시티