파이썬 연산자 바인딩이란 무엇일까요?
연산자 바인딩(operator binding)은 파이썬이 a == b처럼 연산자를 사용한 표현식을 만났을 때, 그 연산자를 실제 객체의 메서드 호출로 연결하는 내부 동작 방식을 의미합니다. 파이썬에서 모든 연산자는 사실 특별한 이름(던더 메서드, dunder method)을 가진 메서드 호출로 변환됩니다.
기본 동작 순서: a == b는 어떻게 처리될까?
다음과 같은 표현식이 있을 때,
a == b
파이썬 인터프리터는 아래 순서대로 처리합니다.
- 먼저 객체
a에서__eq__()메서드를 찾습니다. - 해당 메서드가 존재하면,
b를 인자로 전달하여 호출합니다. 즉,a.__eq__(b)를 실행합니다. - 만약 이 메서드가
NotImplemented를 반환한다면, 이번에는 반대 방향으로 시도합니다. 즉,b.__eq__(a)를 호출합니다. - 양쪽 모두
NotImplemented를 반환하면, 최종적으로 기본 동작(객체 정체성 비교)에 따라 결과가 결정됩니다.
# 1단계: a.__eq__(b) 호출
# 2단계: NotImplemented 반환 시 b.__eq__(a) 호출
NotImplemented의 역할
NotImplemented는 에러가 아니라 파이썬의 특수한 상수입니다. "내 클래스는 이 타입과의 비교 방법을 모르니, 상대 객체에게 기회를 넘겨달라"는 신호로 사용됩니다. 덕분에 서로 다른 클래스 간의 비교나 연산도 유연하게 처리할 수 있습니다.
직접 구현해 보는 예제
아래 예제는 __eq__() 메서드를 직접 구현하여 연산자 바인딩이 어떻게 작동하는지 보여줍니다.
class Vector:
def __init__(self, x):
self.x = x
def __eq__(self, other):
if isinstance(other, Vector):
return self.x == other.x
return NotImplemented # 비교 불가 타입이면 반대쪽에 기회를 넘김
a = Vector(10)
b = Vector(10)
print(a == b) # True → a.__eq__(b)가 호출됨
산술 연산자도 같은 원리로 동작합니다
비교 연산자뿐 아니라 산술 연산자도 동일한 바인딩 규칙을 따릅니다. 예를 들어 a + b는 먼저 a.__add__(b)를 시도하고, NotImplemented가 반환되면 역방향 메서드인 b.__radd__(a)를 호출합니다. 이런 역방향(reflected) 메서드에는 __radd__, __rsub__, __rmul__ 등이 있습니다.
정리
파이썬의 연산자 바인딩은 단순히 겉보기 연산자를 메서드 호출로 바꾸는 것을 넘어, 좌변부터 시도하고 실패하면 우변으로 넘어가는 체계적인 폴백(fallback) 구조를 가집니다. 이 원리를 이해하면 사용자 정의 클래스에서 연산자 오버로딩을 훨씬 자연스럽고 안전하게 구현할 수 있습니다.