본문 바로가기
파이썬

파이썬의 던더 메서드

by 왕초보 독학 코딩 2024. 1. 7.

파이썬 객체를 제대로 사용하고 싶다면 던더 메서드와 파이썬의 데이터 모델에 대해서 잘 알아야 한다. 던더 메서드를 구현하면 사용자 정의 객체도 내장형 객체처럼 작동하게 되어, 파이썬스러운 표현력 있는 코딩 스타일을 구사할 수 있다.

 

 

 

데이터 모델이란?

파이썬의 데이터 모델은 객체의 규칙과 구조라고 생각하면 된다.

 

파이썬에서 문자열의 길이를 알고 싶다면 len(name) 처럼 len() 함수에 객체를 전달하는 방식이지만, 다른 언어들은 대부분 name.length()와 name.size() 처럼 객체의 메서드를 사용하는 방식을 사용한다. 

 

파이썬의 객체의 길이를 알고 싶다면 len() 함수에 객체를 전달하는 동일한 방식이 사용되므로 새로운 클래스의 객체를 사용하더라도 일관성이 큰 장점이 된다. 하지만 다른 언어는 객체의 길이를 알고 싶다면 해당 클래스의 메서드를 잘 알아야 한다.

 

 

 

던더 메서드이란?

파이썬의 데이터 모델을 통해서 일관성은 던더 메서드를 구현함으로써 만들어진다고 할 수 있다. 던더 메서드는 두 개의 언더바로 감싸진 메서드를 의미하는데, 더블 언더 메서드, 매직 메서드, 특별 메서드 등 다양한 이름으로 불린다. 여기서는 던더 메서드라고 부르겠다.

 

예를 들어, 사용자 정의 클래스가 len() 함수에서 작동하게 하려면, __len__() 메서드를 구현하면 된다.

class MyList:
    def __init__(self, items):
        self.items = items
    
    def __len__(self):
        return len(self.items)
my_list = MyList([1, 2, 3])
print(len(my_list))  # 3

 

던더 메서드는 이외에도 __init__, __repr__, __str__, __abs__, __getitem__ 등 정말 다양하게 있다.

 

 

 

던더 메서드는 어떻게 사용되나?

던더 메서드는 개발자가 아니라 파이썬 인터프리터가 호출하기 위한 것임을 기억해야 한다. my_list.__len__()으로 직접 호출하지 않고, len(my_list) 형태로 호출해야 한다. 


list, str, bytearray 등과 같은 내장 자료형의 객체를 len() 함수에 전달하면, 파이썬 인터프리터는 __len__을 호출하지 않고 ob_size 필드의 값을 이용해서 더 효율적인 방식을 사용한다. 

 

그리고 던더 메서드는 암묵적으로 호출된다. 예를 들어 for i in x의 경우 실제로는 iter(x)를 호출하며, 이 함수는 다시 x.__iter__()를 호출한다. iter(x)를 명시적으로 호출하지 않았지만 파이썬 인터프리터가 iter(x)를 암묵적으로 호출된 경우이다.

 

일반적으로 개발자가 던더 메서드를 호출하는 경우는 자식 클래스에서 슈퍼 클래스의 __init__() 메서드를 호출하는 경우가 유일하다고 볼 수 있다.

class Parent:
    def __init__(self, value):
        self.value = value

class Child(Parent):
    def __init__(self, value, additional_value):
        super().__init__(value)
        self.additional_value = additional_value

 

 

 

던더 메서드를 통해 데이터 모델을 사용할 때의 장점

던더 메서드를 통해 데이터 모델을 사용할 때의 장점은 크게 2가지로 분류된다.

1. 객체의 동작을 일관되고 예측 가능하게 만든다.

2. 파이썬 표준 라이브러리에서 제공하는 풍부한 기능을 별도로 구현할 필요 없이 바로 사용할 수 있다.

 

이전의 설명처럼 __len__() 메서드를 구현되어 있는 객체의 길이는 len() 함수를 사용하면 구할 수 있다고 했다. 그러므로 모든 객체의 동작을 일관되고 예측가능하게 만들어진다는 것을 알 수 있었다.

 

그럼 던더 메서드를 통해 파이썬 표준 라이브러리에서 제공하는 풍부한 기능을 별도로 구현할 필요 없이 바로 사용하는 방법에 대해서 예제를 통해서 자세히 알아보자.

 

 

 

던더 메서드를 통해 표준 라이브러리의 기능 사용하기

먼저 예제로 사용할 클래스를 만들어 보자. __len__()과 __getitem__() 메서드를 정의한다.

class MyList:
    def __init__(self):
        self.items = list(range(0, 10)
        
    def __len__(self):
        return len(self.items)
        
    def __getitems__(self, index):
        return self.items[index]

 

MyList 객체는 0~9의 숫자를 가지는 리스트가 된다. __len__()를 구현하였기 때문에 len() 함수를 통해서 MyList 객체의 길이를 얻을 수 있고, __getitems__()를 구현하였기 때문에 서브스크립트로 특정 인덱스의 값을 얻을 수 있다.

my_list = MyList()
print(len(my_list))  # 10
print(my_list[0])  # 0

 

만약 임의의 값을 얻는다면 메서드를 정의해야 할까? 그렇지 않다. 파이썬은 리스트와 튜플같은 시퀀스에서 무작위로 요소를 골라내는 random.choice()라는 함수를 제공한다. random.choice() 함수에 전달하는 객체에 __len__() 메서드와 __getitem__() 메서드가 정의되어 있으면 해당 객체에서 임의의 값을 얻을 수 있게 된다.

MyList는 __len__() 메서드와 __getitem__() 메서드가 정의되어 있으니, 추가로 메서드를 정의하지 않고도 random.choice() 함수로 임의의 값을 얻을 수 있게 된다. 

import random

my_list = MyList()
print(random.choice(my_list))  # 임의의 값 출력

 

random.choice() 외에도 sum(), max(), min() 등 다양한 내장 함수를 사용할 수 있다.

print(sum(my_list))  # 45
print(max(my_list))  # 9
print(min(my_list))  # 0

 

 

 

어떤 던더 메서드를 구현해야 할까?

던더 메서드의 종류는 정말 많다. 따라서 어떤 것을 구현해야 하는지는 클래스가 특정 동작을 지원하고자 할 때에 따라 달라진다. 나는 직접 코드로 실행해서 발생하는 오류 구문을 참고해서 필요한 던더 메서드를 구현하는 것이 가장 쉬운 방법이라고 생각한다.

 

만약 다음과 같이 Card 클래스를 정의하고, 서브스크립트를 시도하면 에러가 발생한다.

class Card:
    pass
card = Card()
print(card[0])

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'Card' object is not subscriptable

 

에러가 발생한 내용만으로는 던더 메서드를 유추할 수 없다. 하지만 이 에러 내용을 구글링 해보면 __getitem__을 구현하라는 정보를 얻을 수 있다.

class Card:
    def __getitem__(self, index):
        return index
card = Card()
print(card[10])  # 10

 

 

'파이썬' 카테고리의 다른 글

파이썬 namedtuple  (1) 2024.01.07
파이썬 언패킹  (0) 2024.01.01
파이썬의 str과 repr의 차이점  (0) 2024.01.01