데이터과학 삼학년

Magic method (매직 메서드) 본문

Python

Magic method (매직 메서드)

Dan-k 2020. 4. 6. 21:05
반응형

Magic method

파이썬 class 안에 메서드를 입력할때 일명 던더(더블언더바) 함수를 붙이는 경우가 있다.

가령

__init__ , __str__ 등이 그렇다.

여기서 던더함수는 가장 대표적인 매직 메서드이다. 즉 던더함수를 쓰면 어떤 정해진 틀에 해당 함수를 정의한다. 라고 보면 된다.

예를 들어

__init__ : 해당 class명으로 instance를 만들때 초기 설정값

__str__, __repr__ : print() 함수를 쓸 때 호출할 값 설정

 

이다.

아래 코드처럼 __init__ 을 사용하여 class의 instance를 만들때 넣어줄 초기값을 지정하는 것과 같다.

즉, 함수명은 __init__ 으로 지정하였지만 실제 해당 함수를 호출할때는 클래스명() 로 

정의한 함수명으로 호출하지 않는다는 것이다. --> 즉 던더함수는 호출할 함수명이 미리 정의되어있다. 

# -*- coding: utf-8 -*-

class Dog(object):
    def __init__(self, name, age):
        print('이름: {}, 나이: {}'.format(name, age))
        
dog_1 = Dog('Pink', '12')

여기서 + 연산자의 경우는 __add__ 로 함수를 정의해주면 되는데, 

이렇게 매직 메서드를 호출하여 오버로드하는 방식이 class에서 던더함수를 쓰는 이유이다.

 

기본적으로 class에서 어떠한 instance를 만들었을때, 해당 instance가 가지고 있는 매직메소드를 확인하는 방법은 dir() 함수를 사용하는 것이다.

# -*- coding: utf-8 -*-

# int를 부모 클래스로 가진 새로운 클래스 생성
class MyInt(int):
    pass

# 인스턴스 생성
my_num = MyInt(5)

# 매직 메소드를 가지고 있는지 확인
print(dir(my_num))


====================
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', 
'__delattr__', '__dict__', '__dir__', '__divmod__', '__doc__', '__eq__', 
'__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', 
'__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__int__', '__invert__',
'__le__', '__lshift__', '__lt__', '__mod__', '__module__', '__mul__', '__ne__', '__neg__',
'__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__',
'__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__',
'__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__',
'__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', 
'__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 
'numerator', 'real', 'to_bytes']

 

매직 메서드를 이용하여 '+' 연산자에 새로운 기능을 추가해보자

# -*- coding: utf-8 -*-

# int를 부모 클래스로 가진 새로운 클래스 생성
class MyInt(int):
    # __add__ 변경
    def __add__(self, other):
        return '{} 더하기 {} 는 {} 입니다'.format(self.real, other.real, self.real + other.real)

# 인스턴스 생성
my_num = MyInt(5)

print(my_num + 5)


=====================
5 더하기 5 는 10 입니다

이렇게 built-in type인 int, str, list, dict등은 사용자들의 편리함을 위해서 자신에 타입에 맞게 각종 오퍼레이터를 오버로딩하는 매직 메소드를 포함하고 있다. 

매직 메소드를 이용하여 연산을 한 코드는 아래와 가탇.

# -*- coding: utf-8 -*-

# 리스트의 덧셈
print([1,2,3] + [4,5,6])
# 매직 메소드로 덧셈
print([1,2,3].__add__([4,5,6]))

# 사전의 길이 확인
print(len({'one':1, 'two': 2, 'three': 3}))
# 매직 메소드로 사전의 길이 확인
print({'one':1, 'two': 2, 'three': 3}.__len__())

Magic method의 필요성

자! 그럼 class에서 매직 메서드를 정의하여 해당 class를 좀 더 유용하게 써보자.

예를 들면 tensorflow의 * 연산이 matmul로 되어 있는 것도 magic method를 사용한 것이라고 볼 수 있다.

 

일반적으로 class 객체를 만들고 print를 하면 할당된 메모리 주소가 나오게 된다.

# -*- coding: utf-8 -*-

class Food(object):
    def __init__(self, name, price):
        self.name = name
        self.price = price

food_1 = Food('아이스크림', 3000)

# 인스턴스 출력
print(food_1)


==============
<__main__.Food object at 0x101a07710>

하지만 print(객체)를 했을 때 객체의 정보를 보고 싶다면 메직메서드를 정의해줘야만 한다. 바로 아래코드처럼!

# -*- coding: utf-8 -*-

class Food(object):
    def __init__(self, name, price):
        self.name = name
        self.price = price
        
    def __str__(self):
        return '아이템: {}, 가격: {}'.format(self.name, self.price)

food_1 = Food('아이스크림', 3000)

# 인스턴스 출력
print(food_1)


================
템: 아이스크림, 가격: 3000

비교 연산자 역시

매직 메서드를 정의해줘야한다. 그렇지 않으면 단순히 메모리 주소를 비교한 결과가 출력된다.

아래 코드가 매직메서드를 정의하지 않았을때 잘못된 비교 연산을 보여준다.

# -*- coding: utf-8 -*-

class Food(object):
    def __init__(self, name, price):
        self.name = name
        self.price = price

food_1 = Food('아이스크림', 3000)
food_2 = Food('햄버거', 5000)

# food_2가 food_1보다 큰지 확인
print(food_1)
print(food_2)
print(food_1 < food_2)


==============
<__main__.Food object at 0x103cc0b50>
<__main__.Food object at 0x1039b5a90>
False

그렇다면

비교 연산을 내가 원하는 값으로 하려면 아래처럼 매직 메서드를 정의해줘야한다. 비교연산자 역시!

# -*- coding: utf-8 -*-

class Food(object):
    def __init__(self, name, price):
        self.name = name
        self.price = price
        
    def __lt__(self, other):
        if self.price < other.price:
            return True
        else:
            return False

food_1 = Food('아이스크림', 3000)
food_2 = Food('햄버거', 5000)
food_3 = Food('콜라', 2000)

# food_2가 food_1보다 큰지 확인
print(food_1 < food_2)  # 3000 < 5000
print(food_2 < food_3)  # 5000 < 2000


===========
True
False

__add__ 메서드 역시 마찬가지다.

매직 메서드를 정의하지 않고 그저 연산을 하게 되면 TypeError 가 나타나게 될것이다. 메모리주소끼리 더할 수가 없기 때문이다.

아래 코드처럼 __add__ 연산자도 더해줘야한다.

# -*- coding: utf-8 -*-

class Food(object):
    def __init__(self, name, price):
        self.name = name
        self.price = price
        
    def __add__(self, other):
        return self.price + other.price

food_1 = Food('아이스크림', 3000)
food_2 = Food('햄버거', 5000)

print(food_1 + food_2)

생각보다 이렇게 일일히 매직 메서드를 정의해줘야하는 불편함은 있지만.

이렇게 정의해줌으로써 나만의 코드 완성과 프로젝트의 성격에 맞게 코드를 재구성할 수 있는 장점이 있다.

 

 

Magic method 모음

 

Binary Operators

Operator Method
+ object.__add__(self, other)
- object.__sub__(self, other)
* object.__mul__(self, other)
// object.__floordiv__(self, other)
/ object.__div__(self, other)
% object.__mod__(self, other)
** object.__pow__(self, other[, modulo])
>> object.__lshift__(self, other)
<< object.__rshift__(self, other)
& object.__and__(self, other)
^ object.__xor__(self, other)
| object.__or__(self, other)

 

Extended Assignments

Operator Method
+= object.__iadd__(self, other)
-= object.__isub__(self, other)
*= object.__imul__(self, other)
/= object.__idiv__(self, other)
//= object.__ifloordiv__(self, other)
%= object.__imod__(self, other)
**= object.__ipow__(self, other[, modulo])
<<= object.__ilshift__(self, other)
>>= object.__irshift__(self, other)
&= object.__iand__(self, other)
^= object.__ixor__(self, other)
|= object.__ior__(self, other)

 

Unary Operators

Operator Method
- object.__neg__(self)
+ object.__pos__(self)
abs() object.__abs__(self)
~ object.__invert__(self)
complex() object.__complex__(self)
int() object.__int__(self)
long() object.__long__(self)
float() object.__float__(self)
oct() object.__oct__(self)
hex() object.__hex__(self)

 

Comparison Operators

Operator Method
< object.__lt__(self, other)
<= object.__le__(self, other)
== object.__eq__(self, other)
!= object.__ne__(self, other)
>= object.__ge__(self, other)
> object.__gt__(self, other)

출처 : http://schoolofweb.net/blog/posts/%ED%8C%8C%EC%9D%B4%EC%8D%AC-oop-part-6-%EB%A7%A4%EC%A7%81-%EB%A9%94%EC%86%8C%EB%93%9C-magic-method
 

SchoolofWeb :: 파이썬 - OOP Part 6. 매직 메소드 (Magic Method)

파이썬 객체 지향 프로그래밍(Object Oriented Programming) 강좌 - Part 6. 매직 메소드 (Magic Method)

schoolofweb.net

 

728x90
반응형
LIST
Comments