AI/Deep Dive In Python

[2025 하계 모각코] 1W-Python Data Model

nninsu 2025. 7. 7. 16:36

Python의 장점 중 하나는 Consistency로, 이러한 장점에 기반하여 새로운 기능을 유추할 수 있게 된다. 다만, 다른 OOP언어에 비해, `collection.len()` 대신, `len(collection)`과 같이 사용하는 것이 이상하게 느껴지기도 하지만, 정확한 이해를 기반으로 Pythonic한 코드를 알 수 있게 된다. 

Python Data model이란 객체가 가장 idiomatic한 언어 기능으로 잘 작동하도록 만들기 위해 사용한 API를 나타낸다. 데이터 모델을 프레임 워크로서의 파이선에 대한 설명으로 생각할 수 있는데, 이는 언어 자체의 구성 요소들의 인터페이스를 공식화 한다. 어떤 프레임워크로든 코딩시에, 프레임워크에 의해 호출되는 메서드들을 구현하는데 많은 시간을 소요하며, 이는 파이썬 데이터 모델 활용시에도 동일하다. 파이썬 인터프리터는 기본적인 객체 연산을 수행하기 위해 **특수 메서드(Special Method)** 를 호출하며, 이는 종종 특별한 Syntax에 의해 유발된다. 


Special Methods(Dunder)
Special method는 앞 뒤로 두 개의 밑줄이 붙는 이름을 갖는 메서드를 의미한다. special method는 double underscore(abbr. dunder)라고도 불리는데, 이들은 파이썬 인터프리터에 의해 Implicit하게 호출되며, 사용자가 직접 호출하는 경우는 거의 드물다. (예외적으로 `__init__`은 슈퍼 클래스의 initializer를 호출하기 위해 직접 호출될 수 있다.) 예를 들어 `obj[key]`라는 syntax는 `__getitem__`이라는 special method에 의해 지원된다. 이러한 Special Method는 객체가 Sequence, Collection, attribute access, operator over-loading, 함수/method 호출, 객체 생성 및 소멸, 문자열 표현 및 formatting, 컨텍스트 관리 등을 구현 및 지원하고 상호작용할 수 있게 한다.

예시코드를 통해 살펴보자.

import collections

Card = collections.namedtuple('Card', ['rank', 'suit'])

class FrenchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()

    def __init__(self):
        self._cards = [Card(rank, suit) for suit in self.suits
                                        for rank in self.ranks]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, position):
        return self._cards[position]



namedtuple을 통해서 커스텀 메서드 없이 단순한 속성의 묶음인 객체들의 클래스를 생성하는데 사용할 수 있다. FrenchDeck 클래스의 사용을 생각해보자.


각각의 출력문은 `len()`함수와 `__getitem__`메서드가 제공하는 기능이다. 이러한 특수 메서드를 이용해서 파이썬 데이터 모델을 활용하는 두 가지의 장점을 보았다. 
1. 클래스의 사용자가 standard operation을 지원하는 애매한 메소드 명을 기억하지 않아도 된다.(Consistency)
2. 이미 구현된 부분을 굳이 만들 필요가 없이 이용해도 된다.(Reusable)

이 외에도 `__getitem__`의 구현으로 iterable하며, reverse하게 iterable도 가능하다. 이러한 iteration은 대부분 implicit하다. `__contains__`메서드가 없다면, `in`연산자는 sequential scan을 실시한다. 정렬 또한, `sorted(iterable, *,key = None, reverse =False)` 또한 key를 통해 순서만 주어진다면 가능하다.

def spades_high(card):
rank_value = FrenchDeck.ranks.index(card.rank)
return rank_value * len(suit_values) + suit_values[card.suit]


FrenchDeck이 implicit하게 Object를 상속받지만, 기능이 상속된 것이 아닌 데이터 모델의 활용 및 composition으로부터 도출된다. 단지 `__len__`과 `__getitem__`만을 구현하여 Standard python sequence처럼 동작하고, 반복 및 슬라이싱과 같은 핵심 언어기능의 이용 및 표준라이브러리 사용까지 가능해진다.

How Special Method are used?
**dunder는 interpreter에 의해 호출된 것이지, 내가 호출한게 아니다.** 예를들어 내가 만든 객체를 my_object라 하면, `len(my_object)`라 쓰고, my_object가 정의된 클래스의 인스턴스일때, 파이썬은 내가 구현한 len 인스턴스 메소드를 호출한다.

하지만, 내장타입(list, str, bytearray, dictionary 등)의 경우, 인터프리터는 len()의 CPython 구현은 실제로 메모리에서 가변 크기 내장객체(Variable-sized built-in object)로 나타나는 PyVarObject C 구조체의 `ob_size`필드 값을 반환한다. 

대부분의 경우, dunder의 호출은 암시적이다. 예를 들어, `for i in x`문장은 실제로 `iter(x)`의 호출을 야기하고, 이는 차례로 `x.__iter__()`를 호출할 수 있다. 일반적으로는 dunder에 대한 직접적인 호출을 가져서는 안된다. 메타 프로그래밍을 수행하지 않는 이상, dunder를 구현하는 데에 더 집중해야한다. 사용자 코드로서 직접적으로 호출되는 유일한 dunder는 `__init__`뿐이다. 이는 init구현에서 상위 클래스의 initializer를 호출하기 때문이다. 만일 dunder를 호출해야 할 때, `len, iter, str` 같은 내장 함수를 호출하는 것이 더 권장된다.

Why len is not a method?
어떤 객체가 내장 타입의 인스턴스인 경우 `len(x)는 메서드를 호출하지 않는다. 길이에 국한하여 C구조체의 필드에서 읽어온다. 이는 길이를 구하는 연산이 매우 일반적인 연산이고 `str, list, memoryview`와 같은 기본적이고 다양한 타입에게 효율적으로 작동해야하기 때문이다. 
즉, len은 파이썬 데이터 모델의 일부중 특별한 대우를 해주는 셈으로 메서드를 호출하지 않는다. 다만, `__len__`특별 메서드로 인해 len이 사용자 정의 객체들에도 사용할 수 있게 된다. 이는 효율적인 내장 객체들의 필요성과 언어의 일관성 간의 공정한 타협점이다. 

'AI > Deep Dive In Python' 카테고리의 다른 글

[2025 하계 모각코] 2W-Array Of Sequence  (1) 2025.07.08