C.W.K.
Stream
Lesson 03 of 07 · published

Dunder 메서드 1 — __repr__ / __str__ / __eq__ / __hash__

~22 min · dunder, repr, eq, hash

Level 0호기심
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete

클래스를 언어와 통합시키는 메서드들

Dunder 메서드 (이중 언더스코어) 가 Python 빌트인 연산이 클래스와 대화하는 방식. print(obj)obj.__str__ 호출. obj1 == obj2__eq__ 호출. {obj: 1}__hash__ 호출. 적절한 dunder 구현이 클래스를 native Python 타입처럼 동작하게.

__repr__ vs __str__ — 명확한 거 vs 친근한 거

__repr__ 은 명확해야 — 이상적으론 평가하면 객체 재생성하는 문자열 — Point(x=3, y=4). REPL 과 디버거 출력에서 보여. __str__ 은 친근한, 사용자용 문자열 — printstr(obj) 가 사용. 하나만 정의하면 __repr__ — Python 이 __str__ 없으면 fallback.

__eq__ — 값 동등성

디폴트 — obj1 == obj2 가 identity (obj1 is obj2). __eq__ override 해서 값으로 비교 — 보통 관련 속성 비교. 두 Point(3, 4) 인스턴스는 다른 객체라도 equal 해야.

__hash__ — dict 키나 set 멤버 되려면 필수

__eq__ override 하면 Python 이 __hash__None 으로 — 인스턴스 hashable 안 됨. 이유 — Python 의 계약 = equal 객체는 같은 hash. 하나 override 하면 일관성 유지해야. 해결책 — __eq__ 와 함께 __hash__ 정의, 보통 hash((self.attr1, self.attr2)). mutable 객체엔 종종 __hash__None 으로 두는 게 맞음 — mutable 은 hash 안 되어야.

원칙: 세 dunder 가 일관된 세트 — __repr__ (디버깅), __eq__ (값 비교), __hash__ (컬렉션 멤버십). 그 순서로 함께 정의 — 클래스가 언어에 깔끔히 통합. 또는 @dataclass 써서 자동 생성 (lesson 6).

Code

Dunder 없이 — 디폴트 동작·python
class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

p = Point(3, 4)
print(p)                      # <__main__.Point object at 0x...>
print(repr(p))                # 같음

p2 = Point(3, 4)
print(p == p2)                # False    <- identity 비교, 값 X

try:
    {p}                       # 디폴트 __hash__ 가 id() 사용 — 작동, 무용지물
except TypeError as e:
    print(e)
Dunder 와 함께 — 언어가 동참·python
class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __repr__(self):
        return f"Point(x={self.x}, y={self.y})"

    def __str__(self):
        return f"({self.x}, {self.y})"

    def __eq__(self, other):
        if not isinstance(other, Point):
            return NotImplemented
        return self.x == other.x and self.y == other.y

    def __hash__(self):
        return hash((self.x, self.y))

p = Point(3, 4)
p2 = Point(3, 4)
p3 = Point(5, 6)

print(repr(p))          # 'Point(x=3, y=4)'
print(str(p))           # '(3, 4)'
print(p == p2)          # True
print(p == p3)          # False
print({p, p2, p3})      # {Point(x=3, y=4), Point(x=5, y=6)}    — dedup 됨
NotImplemented vs False — 비교 불가일 때·python
class Money:
    def __init__(self, amount, currency):
        self.amount, self.currency = amount, currency

    def __eq__(self, other):
        if not isinstance(other, Money):
            return NotImplemented       # Python 이 *반대쪽* 시도하게
        if self.currency != other.currency:
            return False                # 같은 타입, equal X
        return self.amount == other.amount

m1 = Money(100, "USD")
m2 = Money(100, "USD")
m3 = Money(100, "KRW")
print(m1 == m2)          # True
print(m1 == m3)          # False
print(m1 == "100 USD")   # False — Python 이 NotImplemented 보고 str.__eq__ 시도
Mutable 클래스 — __hash__ 를 None 으로 두기·python
class Cart:
    def __init__(self):
        self.items = []

    def __eq__(self, other):
        return isinstance(other, Cart) and self.items == other.items

    # __hash__ 설정 X — Python 이 __eq__ override 시 자동 None
    # Cart 는 set / dict 키 X. 맞음 — mutable 은 hash 안 되어야.

c = Cart()
c.items.append("apple")

try:
    {c}
except TypeError as e:
    print(e)             # unhashable type: 'Cart'

External links

Exercise

클래스 Card(rank, suit) 구현 — 트럼프 카드. __repr__ 'Card(rank=Q, suit=hearts)' 표시, __str__ 'Q♥' 표시 (suit 이름 → 심볼 dict 사용), __eq__ rank + suit 비교, __hash__ 로 set 가능. 작은 덱 만들어 — 다섯 장, 한 장 중복 — set 에 넣고 중복 제거 확인. 각 카드 repr + str 둘 다 출력.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.