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

Dunder 메서드 2 — 연산자 오버로딩 + 컨테이너 동작

~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

연산자 오버로딩 — 각 심볼이 부르는 거

Python 의 모든 연산자가 dunder 매핑. +__add__. -__sub__. *__mul__. <__lt__. 전체 list 는 data model 에. 구현하면 커스텀 Vector 클래스에 v1 + v2 가능. Reflected 버전 (__radd__ 등) 은 클래스가 연산자 오른쪽에 있고 왼쪽이 다른 타입일 때.

컨테이너 dunder — len / getitem / contains / iter

__len__len(obj) 작동시켜. __getitem__obj[i]. __contains__x in obj. __iter__for x in obj. 정수 인덱스로 __getitem__ 구현하면 — 무료로 반복도. Python 이 __getitem__(0), __getitem__(1), ... IndexError 까지 fallback.

__bool__ — 객체의 truthiness

__bool__ 없으면 Python 은 __len__ 정의됐으면 사용 (길이 0 이 falsy), 아니면 모든 인스턴스 truthy. 둘 다 디폴트 안 맞을 때 __bool__ 정의. 메서드는 bool 반환해야.

__call__ — 인스턴스를 함수처럼

__call__ 정의 + my_instance(args) 가능. 어디서나 사용 — 클래스 기반 decorator, state 가진 함수 객체, callable 전략. decorators 트랙에서 봤어.

원칙: 연산자 오버로딩은 연산이 타입에 자연스러운 의미일 때 좋아 (Vector + Vector, Money + Money). 강제하거나 모호할 때 나빠 (User + User?). 편향 — 독자가 타입만 봐서 a + b 가 뭐 할지 못 예측하면 — 오버로드 X.

Code

Vector + 연산자 오버로딩·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__ 덕분
컨테이너 dunder — 클래스를 iterable 하게·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)
비교 dunder — 객체 정렬·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
__bool__ + __call__·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 구현 — 계수 list 로 다항식 (상수항 먼저). __repr__ (예 'Polynomial([1, 0, 2])' = 1 + 0x + 2x²), __add__ (계수 페어 더한 새 Polynomial 반환, 짧은 거 0 으로 패딩), __call__(x) 로 x 에서 평가. 덧셈 + 평가 테스트 — (Polynomial([1, 2, 3]) + Polynomial([0, 1]))(2)1 + 3*2 + 3*4 = 19 여야.

Progress

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

댓글 0

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

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