Python 연산자 오버로딩 마스터하기: 실전 가이드
Python 연산자 오버로딩
Python 내장 연산자는 피연산자 타입에 따라 다르게 동작. + 연산자는 숫자 더하기, 문자열 연결, 리스트 병합 등 다양. 이 유연함은 연산자 오버로딩으로 달성—자신 클래스에 문맥별 연산자 의미 부여 기능.
2D 평면 좌표 표현하는 간단한 Point 클래스 고려. 특수 메서드 없으면 두 포인트 더하기 시도 시 TypeError:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
p1 = Point(1, 2)
p2 = Point(2, 3)
print(p1+p2)
출력
Traceback (most recent call last): File "<string>", line 9, in <module> print(p1+p2) TypeError: unsupported operand type(s) for +: 'Point' and 'Point'
Python이 두 Point 인스턴스 결합 방법을 모르니 예외 발생. 연산자 오버로딩으로 이 행동 직접 정의 가능.
Python 특수 메서드
이중 언더스코어로 둘러싸인 메서드—__init__, __str__, __add__ 등—을 특수 메서드 또는 "더블 언더(dunder)" 메서드라 함. Python 런타임이 암시적 호출하며 연산자 오버로딩, 컨테이너 프로토콜 등의 훅 포인트.
예: __str__ 메서드는 객체의 사람이 읽기 좋은 문자열 변환 제어. 기본적으론 객체 출력 시 메모리 주소 표시돼 유용하지 않음. __str__ 오버라이드로 디버깅/로깅 훨씬 명확:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return '({0}, {1})'.format(self.x, self.y)
이제 Point 출력 시:
p1 = Point(2, 3)
print(p1)
출력
(2, 3)
같은 표현이 str(p1)과 format(p1)에서도 쓰임—Python 내부적으로 p1.__str__() 호출.
+ 연산자 오버로딩
Point 객체 덧셈 커스터마이즈 위해 __add__ 구현:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return '({0}, {1})'.format(self.x, self.y)
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
새 행동 테스트:
p1 = Point(1, 2)
p2 = Point(2, 3)
print(p1 + p2)
출력
(3, 5)
내부적으로 Python이 p1.__add__(p2) 실행해 합산 좌표 가진 새 Point 반환.
다른 산술 연산자와 구현 필요 특수 메서드 빠른 참조:
| 연산자 | 표현식 | 특수 메서드 |
| 덧셈 | p1 + p2 | __add__(self, other) |
| 뺄셈 | p1 - p2 | __sub__(self, other) |
| 곱셈 | p1 * p2 | __mul__(self, other) |
| 거듭제곱 | p1 ** p2 | __pow__(self, other) |
| 나눗셈 | p1 / p2 | __truediv__(self, other) |
| 버림 나눗셈 | p1 // p2 | __floordiv__(self, other) |
| 모듈로 | p1 % p2 | __mod__(self, other) |
| 좌측 시프트 | p1 << p2 | __lshift__(self, other) |
| 우측 시프트 | p1 >> p2 | __rshift__(self, other) |
| 비트 AND | p1 & p2 | __and__(self, other) |
| 비트 OR | p1 | p2 | __or__(self, other) |
| 비트 XOR | p1 ^ p2 | __xor__(self, other) |
| 비트 NOT | ~p1 | __invert__(self) |
비교 연산자 오버로딩
연산자 오버로딩은 산술에 국한 안 됨. 비교 연산자도 맞춤 가능. 예: 원점 거리 기준으로 포인트 비교 위해 __lt__ 오버라이드:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return '({0}, {1})'.format(self.x, self.y)
def __lt__(self, other):
self_mag = self.x**2 + self.y**2
other_mag = other.x**2 + other.y**2
return self_mag < other_mag
사용:
p1 = Point(1, 1)
p2 = Point(-2, -3)
p3 = Point(1, -1)
print(p1 < p2) # True
print(p2 < p3) # False
print(p1 < p3) # False
다른 비교 메서드도 유사 패턴:
| 연산자 | 표현식 | 특수 메서드 |
| 작음 | p1 < p2 | __lt__(self, other) |
| 작거나 같음 | p1 <= p2 | __le__(self, other) |
| 같음 | p1 == p2 | __eq__(self, other) |
| 다름 | p1 != p2 | __ne__(self, other) |
| 큼 | p1 > p2 | __gt__(self, other) |
| 크거나 같음 | p1 >= p2 | __ge__(self, other) |
이 메서드들을 신중히 정의하면 커스텀 객체가 산술/비교 문맥에 자연스럽게 참여해 코드 가독성과 유지보수성 향상.