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

연산자와 컨테이너 규약에 참여하기

~22 min · dunder, operator-overloading, len, getitem, iter, contains

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

특별 메서드는 새 문법을 만드는 게 아니야

a + b는 a의 __add__ 같은 규약을 호출해. 지원하지 않는 상대라면 NotImplemented를 돌려 반대편의 반사 연산 기회를 주고, 새 객체를 만들지 제자리에서 바꿀지도 타입의 의미에 맞춰 정해.

컨테이너처럼 보이면 컨테이너 규약을 지켜

__len__, __getitem__, __iter__, __contains__를 알맞게 정의하면 len, 인덱스, for, in이 자연스럽게 동작해. 일부 규약만 흉내 내어 기대를 깨지 않게 해.

비교·참거짓·호출도 같은 방식이야

정렬에는 비교 계약을, 조건식에는 __bool__ 또는 길이를, 함수처럼 쓰는 객체에는 __call__을 제공할 수 있어. 문법보다 객체의 의미가 먼저야.

Code

벡터가 덧셈에 참여하는 방법·python
class Vector:
    def __init__(self, x, y):
        self.x, self.y = x, y

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

    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y)

    def __sub__(self, other):
        return Vector(self.x - other.x, self.y - other.y)

    def __mul__(self, scalar):
        return Vector(self.x * scalar, self.y * scalar)

    __rmul__ = __mul__       # 3 * v 도 v * 3 처럼 지원

v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2)          # Vector(4, 6)
print(v2 - v1)          # Vector(2, 2)
print(v1 * 3)           # Vector(3, 6)
print(2 * v2)           # Vector(6, 8) — __rmul__ 덕분
객체를 컨테이너처럼 만들기·python
class Deck:
    def __init__(self, cards):
        self._cards = list(cards)

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, i):
        return self._cards[i]

    def __contains__(self, card):
        return card in self._cards

# 이제 Deck 이 시퀀스처럼
d = Deck(["A♥", "K♦", "Q♣", "J♠"])
print(len(d))            # 4
print(d[0])              # 'A♥'
print(d[-1])             # 'J♠'
print("K♦" in d)         # True

# 무료 반복 — 정수로 __getitem__ 이 줘
for card in d:
    print(card)
비교 규약으로 객체 정렬하기·python
from functools import total_ordering

@total_ordering              # __eq__ + __lt__ 에서 6 비교 op 다 줘
class Score:
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        if not isinstance(other, Score):
            return NotImplemented
        return self.value == other.value

    def __lt__(self, other):
        if not isinstance(other, Score):
            return NotImplemented
        return self.value < other.value

    def __repr__(self):
        return f"Score({self.value})"

scores = [Score(85), Score(92), Score(70), Score(100)]
print(sorted(scores))         # value 로 정렬
print(max(scores))            # Score(100)
print(Score(50) < Score(60))  # True
조건식과 호출에 참여하기·python
class Inbox:
    def __init__(self):
        self.messages = []

    def __bool__(self):
        return len(self.messages) > 0

    def __call__(self, msg):
        self.messages.append(msg)

inbox = Inbox()
if inbox:
    print("메시지 있음")
else:
    print("비어있음")           # 이 출력

inbox("hello")              # __call__ — 메시지 추가
inbox("world")

if inbox:
    print(f"{len(inbox.messages)} 개 메시지")

External links

Exercise

상수항부터 계수를 담는 Polynomial을 만들고 repr, 덧셈, 호출 평가를 구현해. 길이가 다른 계수는 0으로 채워 더하고 (Polynomial([1, 2, 3]) + Polynomial([0, 1]))(2)가 19인지 확인해.

Progress

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

댓글 0

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

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