Object-Oriented Programming - Magic Methods
Contents
- Operator Overloading
- Magic Methods
Operator Overloading
첫 코딩 언어로 파이썬이 많이 추천되는 이유는 간단하고 직관적인 문법 덕이다.
a = 1
b = 2
c = [2,2]
d = ['hello',3]
h = 'h'
i = 'i'
>>> print(a+b)
3
>>> print(c+d)
[2, 2, 'hello', 3]
>>> print(h+i)
hi
파이썬에선 + 연산으로 다양한 자료구조를 더할 수 있다. 가장 직관적으로 숫자를 더할 수 있고, 나아가 리스트와 문자열도 더해준다. 하지만 같은 + 연산으로 각 자료구조가 더해지는 방식은 다르다. 두개의 리스트는 하나의 리스트로 통합되고, 두개의 문자열은 하나로 이어진다. 이는 operator overloading 이란 개념으로 작동된다.
>>> type(a)
<class 'int'>
>>> dir(a)
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__']
>>> type(h)
<class 'str'>
>>> dir(h)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__']
위 예시를 보자. a, h
는 각각 int, str
타입의 자료구조다. 이때 dir()
함수에 변수를 인자로 넘기면 실제로 해당 자료구조가 실행 할 수 있는 연산들이 출력된다.
>>> a+b
3
>>> a.__add__(b)
3
>>> a+b == a.__add__(b)
True
즉, 두 정수를 더하는 연산인 a+b
는 사용자의 편의를 위한 연산문이며, 실제로 코드상으로 실행되는 연산은 a.__add__(b)
인 것이다. 두 연산의 bool operand 확인 시 True
가 이를 증명한다.
하지만 이때 dir()
의 출력문을 자세히 보자. 정수인 a
와 문자열인 h
는 둘다 __add__
라는 메서드가 존재하고, 이는 객체지향적 관점의 메서드가 맞다. 파이썬의 모든 자료구조는 파이썬 내부의 클래스로 정의된 것이며, int, str
각각 독립적인 클래스다. __add__
는 각 클래스 속 정의된 메서드이며, 이렇게 double underscore 을 지닌 메서드를 매직 메서드, 혹은 dunder method 라고도 한다.
이런 매직 메서드는 각 연산자가 작동하는 방식을 ‘오버로딩’, 상황에 맞게 바꾸는 역할을 한다. 즉, 정수에 + 연산자를 수행 할 때 int
클래스 내부에 정의된 __add__
메서드가 수행되는 것이다. int
클래스와 str
클래스 내부에 __add__
메서드의 리턴값이 다르기 때문에 같은 + 연산자를 수행해도 자료구조에 따라 결과가 다른 것이다.
매직 메서드를 통한 연산자 오버로딩으로 우린 클래스의 인스턴스를 자유롭게 사용할 수 있다.
Magic Methods
파이썬엔 각 연산자에 해당되는 다양한 매직 메서드가 있다. 대표적으로 + 는 __add__
, - 는 __sub__
등이 있으며, 파이썬의 builtin 함수들도 오버로딩 할 수 있는 매직 메서드가 존재한다.
class Student:
def __init__(self, first, last, health):
self.first = first
self.last = last
self.health = health
self.email = first + '.' + last + '@korea.ac.kr'
def fullname(self):
return f'{self.first} {self.last}'
stu_1 = Student('Tom', 'Lee', 100)
stu_2 = Student('Ian', 'Goodfellow', 100)
>>> print(stu_1)
<__main__.Student object at 0x10158a670>
위 예시를 보자. stu_1
을 print()
하면 해당 인스턴스의 메모리 위치가 출력된다. 하지만 print
는 파이썬에서 가장 많이 사용되는 함수 중 하나이며, 클래스의 사용자 또한 가장 많이 찾는 함수이다. 그렇기에 인스턴스를 print
할때 메모리 위치가 아닌, 인스턴스의 중요한 정보를 출력하도록 할 수 있을까?
class Student:
def __init__(self, first, last, health):
self.first = first
self.last = last
self.health = health
self.email = first + '.' + last + '@korea.ac.kr'
def fullname(self):
return f'{self.first} {self.last}'
def __repr__(self):
return f'Instance of Class Student: {self.fullname()}, {self.email}'
stu_1 = Student('Tom', 'Lee', 100)
stu_2 = Student('Ian', 'Goodfellow', 100)
>>> print(stu_1)
Instance of Class Student: Tom Lee, Tom.Lee@korea.ac.kr
__repr__
메서드를 추가하자 print
가 작동하는 방식이 바뀌었다. __repr__
는 파이썬의 print
함수를 오버로딩 해주는 매직 메서드다. 하지만 보통 __repr__
는 __str__
와 같이 사용된다.
class Student:
def __init__(self, first, last, health):
self.first = first
self.last = last
self.health = health
self.email = first + '.' + last + '@korea.ac.kr'
def fullname(self):
return f'{self.first} {self.last}'
def __repr__(self):
return f'Instance of Class Student: {self.fullname()}, {self.email}'
def __str__(self):
return f'{self.fullname()}, {self.email}'
stu_1 = Student('Tom', 'Lee', 100)
stu_2 = Student('Ian', 'Goodfellow', 100)
>>> print(stu_1)
Tom Lee, Tom.Lee@korea.ac.kr
__str__
메서드를 클래스에 추가하니 print
문이 __str__
에서 정의된 값으로 리턴되도록 바뀌었다. 얼핏 보면 __str__
와 __repr__
는 같은 원리로 동작하기에 하나만 정의해도 될 것 같지만, 보통 클래스를 정의할 때 두 메서드 다 정의한다.
보통 __repr__
는 디버깅과 로깅의 용도로 리턴값을 정의하며, __str__
는 사용자 측면에서 인스턴스를 이해하기 쉽게 돕는 용도로 쓰인다. 그렇기에 __str__
메서드는 필수가 아니지만, __repr__
메서드는 클래스를 정의할 때 무조건적으로 정의하는 것이 좋다.
My rule of thumb:
__repr__
is for developers,__str__
is for customers.