[Python] 연산자 오버로딩

2 분 소요

연산자 오버로딩

class Account:     # 계좌 클래스
    def __init__(self, aid, abl):
        self.aid = aid       # 계좌 번호
        self.abl = abl       # 계좌 잔액
        
    def __add__(self, m):    # 입금
        self.abl += m
        print('__add__')

    def __sub__(self, m):     # 인출
        self.abl -= m
        print('__sub__')

    def __call__(self):      # 계좌 상황을 문자열로 반환
        print('__call__')
        return str(self.aid) + ':' + str(self.abl)


def main():
    acnt = Account('James01', 100)     # 계좌 개설
    acnt + 100     # 100원 입금
    # acnt.__add__(100)
    
    acnt - 50      # 50원 인출
    # acnt.__sub__(50)
    
    print(acnt())
    # print(acnt.__call__())

main()
__add__
__sub__
__call__
james01 : 150

위 코드에서 +와 같은 연산을 진행했을 때 이를 대신해서 미리 약속해 놓은 메소드가 호출되도록 하는 것이 연산자 오버로딩이다. 이러한 연산자와 호출되는 메소드의 관계는 약속되어있고 그 약속을 기반으로 위의 예제가 작성되었다.

class Vector:     
    def __init__(self, x, y):
        self.x = x       
        self.y = y       
        
    def __add__(self, o):    # 벡터의 덧셈 연산
        return Vector(self.x + o.x, self.y + o.y)
 
    def __call__(self):      # 벡터 정보를 문자열로 반환
        return 'Vector({0}, {1})'.format(self.x, self.y)


def main():
    v1 = Vector(3, 3)
    v2 = Vector(7, 7)
    v3 = v1 + v2
    print(v1())
    print(v2())
    print(v3())

main()
Vector(3,3)
Vector(7,7)
Vector(10,10)

+연산자를 적절히 오버로딩한 예이다.

메소드 str 정의

class Vector:     
    def __init__(self, x, y):
        self.x = x       
        self.y = y       
        
    def __add__(self, o):    # 벡터의 덧셈 연산
        return Vector(self.x + o.x, self.y + o.y)
 
    def __str__(self):      # 벡터 정보를 문자열로 반환
        return 'Vector({0}, {1})'.format(self.x, self.y)


def main():
    v1 = Vector(3, 3)
    v2 = Vector(7, 7)
    v3 = v1 + v2
    print(v1)
    print(v2)
    print(v3)

main()
Vector(3,3)
Vector(7,7)
Vector(10,10)

앞에서는 객체가 갖고 있는 정보를 출력하기 위해 call 메소드를 정의했다. 이때 객체가 갖는 정보를 단순히 눈으로 확인하는 정도의 목적이라면 str 메소드를 사용하면 된다. str 메소드는 문자열을 반환하도록 정의해야 한다.

in-place 형태의 연산자 오버로딩

class Vector:     
    def __init__(self, x, y):
        self.x = x       
        self.y = y       
        
    def __add__(self, o):    # 벡터의 덧셈 연산
        return Vector(self.x + o.x, self.y + o.y)
 
    def __str__(self):      # 벡터 정보를 문자열로 반환
        return 'Vector({0}, {1})'.format(self.x, self.y)


def main():
    v1 = Vector(2, 2)
    v2 = Vector(7, 7)

    print(v1, id(v1))
    v1 += v2       # v1 = v1.__add__(v2)
    print(v1, id(v1))

main()
Vector(2,2) 51761616
Vector(9,9) 54451088

정수와 문자열 같이 수정불가능한 immutable 객체라면 += 연산을 하면 v1에 다른 객체가 저장된다.

하지만 리스트와 같이 muutable 객체라면 +=연산을하면 저장된 위치가 달라지지 않는다.

add : + 연산에 대한 오버로딩 iadd : += 연산에 대한 오버로딩

class Vector:     
    def __init__(self, x, y):
        self.x = x       
        self.y = y       
        
    def __add__(self, o):    # 벡터의 + 연산
        return Vector(self.x + o.x, self.y + o.y)

    def __iadd__(self, o):    # 벡터의 += 연산
        self.x += o.x
        self.y += o.y
        return self
 
    def __str__(self):      # 벡터 정보를 문자열로 반환
        return 'Vector({0}, {1})'.format(self.x, self.y)


def main():
    v1 = Vector(2, 2)
    v2 = Vector(7, 7)

    print(v1, id(v1))     # v1과 v1에 저장된 객체의 주소 정보 출력
    v1 += v2       # v1 = v1.__iadd__(v2)
    print(v1, id(v1))     # v1과 v1에 저장된 객체의 주소 정보 출력

main()
Vector(2,2) 52023728
Vector(9,9) 52023728

iadd 메소드를 정의하지 않고 add 메소드만 정의하면 +연산과 +=연산 모두에 있어서 이 메소드가 호출된다. 그러나 iadd 메소드를 추가로 정의하면 +연산시에는 add 메소드가 호출되고, +=연산시에는 iadd 메소드가 호출된다.