2 minute read

본 포스팅은 “윤성우의 열혈 파이썬 중급편” 책 내용을 기반으로 작성되었습니다. 잘못된 내용이 있을 경우 지적해 주시면 감사드리겠습니다.

20-1. 연산자 오버로딩 간단히 이해하기

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  # == anct.__add__(100)
  acnt - 50   # == acnt.__sub__(50)
  print(acnt())   # == print(acnt.__call__())

main()
(결과) __add__
       __sub__
       __call__
       James01:150

acnt + 100 이 __add__ 호출로 이어지는 것! 이것이 연산자 오버로딩 이다!

20-2. 적절한 형태로 +와 - 연산자 오버로딩

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)

예제처럼 약간 +와 -의 의미가 변형되더라도, 그 뜻이 통하도록 연산자 오버로딩이 동작되어야 한다!

20-3. 메소드 __str__의 정의

__str__은 문자열이 반환되도록 정의되어야 한다.
또한, 이 문자열은 해당 객체의 정보를 담고 있어야 한다!

class Simple:
  def __init__(self, i):
    self.i = i

s = Simple(10)

print(s)
s.__str__()
(결과) __main__.Simple object at 0x0000024AF2681DD8>
       '__main__.Simple object at 0x0000024AF2681DD8>'

보다시피 __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)

20-4. 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
  print(v1, id(v1))

main()
(결과) Vector(2, 2) 2520917744440
       Vector(9, 9) 2520917745224

+= 연산 수행시, v1에는 다른 객체가 저장된다(주소값이 다름!)
이는 수정 불가능한 immutable 객체이기 때문인데, mutable 객체일 경우 동일한 객체에 저장된다.

n = [1, 2]
id(n)
n += [3]
id(n)  # 연산 후에도 위치가 동일하다!
(결과) 2520910226120
       2520910226120  

그럼 우리가 만든 클래스의 객체는 어떻게 immutable, mutable 객체를 조정할 수 있을까? 바로 __add____iadd__의 차이다!

  • __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 += v2
  print(v1, id(v1))

main()
(결과) Vector(2, 2) 2520917745336
       Vector(9, 9) 2520917745336

+=, -= 같은 연산자들을 가리켜 ‘in-place 연산자’ 라고 한다! in-place 연산자를 오버로딩 할 때, 반드시 self를 반환해야 한다.

20-5. Account 클래스 수정하기

20-1의 Account 클래스 예제를 수정해보자!

class Account:
  def __init__(self, aid, abl):
    self.aid = aid
    self.abl = abl
  def __iadd__(self, m):
    self.abl += m
    return self
  def __isub__(self, m):
    self.abl -= m
    return self
  def __str__(self):
    return '{0}, {1}'.format(self.aid, self.abl)
  
def main():
  acnt = Account('James01', 100)
  acnt += 130
  print(acnt)
  acnt -= 50 
  print(acnt)   

main()
(결과) James01, 230
       James01, 180

Leave a comment