티스토리 뷰

728x90
반응형

파이썬을 쓰다 보면 숫자, 문자열, 리스트 등 기본 타입에 익숙해지죠. 그런데 내가 만든 클래스에도 +- 같은 연산자를 딱 한 줄로 쓰고 싶을 때가 있어요. 이럴 때 유용한 게 바로 연산자 오버로딩(Operator Overloading)입니다.

1. 연산자 오버로딩이 뭐예요?

연산자 오버로딩이란, 클래스에 특별한 매직 메서드(또는 스페셜 메서드)를 정의해서 a + b 같은 연산을 내가 원하는 대로 만들 수 있게 해주는 기능이에요. 예를 들어, 두 개의 벡터를 더할 때 v1 + v2라고 쓰면 내부적으로 v1.__add__(v2)가 호출되는 거죠.

장점:

  • 코드가 더 깔끔해져요. vector1.add(vector2) 대신 vector1 + vector2!
  • 기본 타입과 비슷한 사용성. 숫자나 문자열 쓰듯 자연스럽게 사용할 수 있어요.

2. 주요 매직 메서드 한눈에 보기

+   → __add__(self, other)      덧셈
-   → __sub__(self, other)      뺄셈
*   → __mul__(self, other)      곱셈
/   → __truediv__(self, other)  나눗셈
%   → __mod__(self, other)      나머지
<   → __lt__(self, other)       작다
>   → __gt__(self, other)       크다
==  → __eq__(self, other)       같다
!=  → __ne__(self, other)       같지 않다
&   → __and__(self, other)      비트 AND
|   → __or__(self, other)       비트 OR (예: LCEL 체이닝)
^   → __xor__(self, other)      비트 XOR
-a  → __neg__(self)             단항 마이너스
+a  → __pos__(self)             단항 플러스

이 외에도 __len__, __getitem__ 같은 시퀀스 관련 메서드도 있어요.

3. 예제: 2D 벡터 클래스

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

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

    def __add__(self, other):
        if not isinstance(other, Vector):
            raise TypeError("Vector와 다른 타입은 더할 수 없어요.")
        return Vector(self.x + other.x, self.y + other.y)

# 사용 예시
i = Vector(1, 2)
j = Vector(3, 4)
print(i + j)  # 출력: Vector(4, 6)

위 코드에서 i + j를 실행하면 i.__add__(j)가 호출되어 x, y 값을 각각 더해준답니다.

4. LangChain LCEL에서의 체이닝

특히 재미있는 활용 예로 LangChain LCEL이 있어요. runnable1 | runnable2라고 쓰면 내부에서 runnable1.__or__(runnable2)가 호출되어 두 개의 작업을 쭉 연결해 주죠. 마치 파이프라인을 이어 붙이는 것처럼요.

예시 코드: 간단한 체인 만들기

from langchain.schema import Runnable
from langchain.lc_el import LCEL  # 가상의 패키지 구조 예시

# 간단한 Runnable 클래스 정의
class Echo(Runnable):
    def __init__(self, text: str):
        self.text = text

    def invoke(self, _input=None):
        return self.text

class AddSuffix(Runnable):
    def __init__(self, suffix: str):
        self.suffix = suffix

    def invoke(self, input: str):
        return input + self.suffix

# runnable 객체 생성
echo_hello = Echo("Hello")
suffix_world = AddSuffix(", World!")

# | 연산자로 체이닝
chain = echo_hello | suffix_world

# 실행 결과 확인
result = chain.invoke()
print(result)  # 출력: Hello, World!

다중 체이닝 활용

# 추가로 대문자 변환 Runnable 정의
def uppercase(text: str) -> str:
    return text.upper()

class UpperCase(Runnable):
    def invoke(self, input: str):
        return uppercase(input)

upper = UpperCase()

# 3단 체인: echo -> suffix -> uppercase
three_step_chain = echo_hello | suffix_world | upper
print(three_step_chain.invoke())  # 출력: HELLO, WORLD!

정리: 연산자 오버로딩 덕분에 사용자 정의 객체도 기본 타입처럼 다룰 수 있어요. 매직 메서드 이름만 알면, 원하는 연산자 기능을 자유롭게 추가·변경할 수 있답니다. 이제 여러분도 __add__, __mul__ 등을 슬쩍 정의해 보고, 코드 한 줄로 더 직관적인 표현을 즐겨보세요! 🚀

728x90
반응형