본문 바로가기
C.W.K.
Stream
Lesson 03 of 07 · published

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

개발자용 표현과 사용자용 표현

__repr__은 디버깅에 유용하고 가능하면 객체를 다시 만들 수 있을 만큼 분명한 표현을, __str__은 사람이 읽기 좋은 표현을 맡아. str이 없으면 repr이 대신 쓰여.

같음과 해시는 함께 설계해

__eq__로 같은 값을 정의했다면 같은 객체끼리는 같은 hash가 나와야 해. 다른 타입과 비교할 수 없으면 False를 확정하지 말고 NotImplemented를 돌려 상대편 비교 기회를 남겨.

값이 바뀌는 객체를 set이나 dict 키로 쓰면 위치를 잃을 수 있으므로 보통 해시 불가로 둬. Python도 __eq__만 재정의하면 __hash__를 None으로 만들어.

Code

특별 메서드가 없을 때의 기본 표현·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 비교이며 값 비교가 아님

try:
    {p}                       # 디폴트 __hash__ 가 id() 사용 — 작동, 무용지물
except TypeError as e:
    print(e)
repr·str·eq·hash를 함께 정의하기·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 돌려주기·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                # 같은 타입이지만 동등하지 않음
        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__ 시도
가변 객체는 해시할 수 없게 두기·python
class Cart:
    def __init__(self):
        self.items = []

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

    # __hash__를 설정하지 않음 — Python 이 __eq__ override 시 자동 None
    # Cart 는 set 원소나 dict 키로 쓸 수 없음. 맞음 — mutable 은 hash 안 되어야.

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

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

External links

Exercise

rank와 suit를 가진 Card를 만들고 repr은 Card(rank=Q, suit=hearts), str은 Q♥가 되게 해. 값 동등성과 hash를 구현해 중복 한 장이 있는 다섯 장을 set에 넣고 중복이 사라지는지, repr과 str이 각각 어떻게 보이는지 확인해.

Progress

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

댓글 0

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

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