
1. 들어가며: Python은 어떻게 동작하는가?
Python 코드를 작성할 때, 우리는 자연스럽게 다음과 같은 표현들을 사용한다:
a + b # 두 값을 더한다
len(my_list) # 리스트의 길이를 구한다
my_list[0] # 첫 번째 요소에 접근한다
for x in obj: # 객체를 순회한다
print(x)
이 코드들이 "그냥 작동한다"고 생각할 수 있다.
하지만 Python은 이 연산들을 어떻게 처리하는 걸까?
예를 들어, len([1, 2, 3])을 호출하면 Python은 어떻게 3이라는 답을 알아내는가?
+ 연산자는 숫자도 더하고, 문자열도 이어붙이는데, Python은 이 차이를 어떻게 구분하는가?
이 질문들의 답이 바로 Python Data Model이다.
용어정리
프로그래밍에서 Dispatch는
호출할 함수나 method를 런타임에 결정하여 실행하는 과정 을 가리킴.
2. Python Data Model이란?
Python Data Model은
Python에서 Object가 언어 구문(syntax)과 상호작용하는 방식을 정의한 규칙 체계이다.
쉽게 말해, +, len(), [], for 같은 Python 문법이 실제로 어떤 방식으로 실행되는지를 정의한 약속이다.
핵심 아이디어는 다음과 같다:
Python의 연산자와 내장 함수는 직접 자신이 실행되는 것이 아니라,
관련된 Object에 정의된 special method를 호출하는 방식으로 동작한다.
때문에 Python 에서 dispathch는
- Python interpreter가 특정 연산 등을 수행할 때,
- 해당 object의 Type에서 적절한 method를 찾아 호출하는 과정 이라고 볼 수 있음.
3. Special Method: dunder methods
3.1 Special Method란?
Special method(또는 dunder method, magic method)는 이름이 double underscore(__)로 감싸진 특별한 method이다:
__init__, __len__, __add__, __getitem__, __str__, ...
- 이 method들은 Python interpreter와의 약속이다.
- 특정 연산이 수행될 때, Python은 해당 object에서 약속된 special method를 찾아 호출한다.
3.2 예제: len() 함수의 동작 원리
my_list = [1, 2, 3]
print(len(my_list)) # 3
len(my_list)를 호출하면, Python은 내부적으로 다음을 수행한다:
type(my_list).__len__(my_list)
즉, my_list의 type인 list class에 정의된 __len__ method를 호출한다.
이것이 우리가 직접 custom class를 만들 때 len()을 지원할 수 있는 이유이다:
class MyCollection:
def __init__(self, items):
self.items = items
def __len__(self):
return len(self.items)
c = MyCollection([1, 2, 3, 4, 5])
print(len(c)) # 5
3.3 주요 Syntax와 Special Method 매핑
| Syntax / 함수 | 호출되는 Special Method |
a + b |
type(a).__add__(a, b) |
a - b |
type(a).__sub__(a, b) |
a * b |
type(a).__mul__(a, b) |
len(obj) |
type(obj).__len__(obj) |
obj[i] |
type(obj).__getitem__(obj, i) |
obj[i] = v |
type(obj).__setitem__(obj, i, v) |
obj() |
type(obj).__call__(obj) |
str(obj) |
type(obj).__str__(obj) |
for x in obj |
type(obj).__iter__(obj) |
이 표를 암기할 필요는 없음.
보다 자세한 건 다음 url을 참고:
2023.07.13 - [Python] - [Python] special methods and operator overloading
[Python] special methods and operator overloading
Special Methods사용자가 직접 호출하는 경우가 거의 없고, 간접적으로 호출이 됨.즉, 개발자(사용자)가 over-riding을 통해 구현은 하지만 직접 호출하는 경우가 거의 없고,개발자가 다른 built-in function
ds31x.tistory.com
중요한 건 다음임:
Python의 syntax는
해당 object의 Type에 정의된 special method로 변환되어 실행된다.
4. 일반 Method vs Special Method
Class에는 일반 method와 special method 모두 정의할 수 있다.
이들은 코드상으로는 이름만 다를 뿐이지만, 호출 방식이 근본적으로 다르다.
물론 둘에 대한 dispatch를
Python Data Model에서 정의하고 있음.
4.1 일반 Method: 사용자가 직접 호출
class Calculator:
def add(self, a, b):
return a + b
calc = Calculator()
calc.add(1, 2) # 사용자가 직접 호출
일반 method의 특징:
- 사용자 코드가 직접 호출한다.
- Attribute lookup 규칙을 따른다 (Instance → Class → 상위 Class 순으로 탐색).
- Instance에 동적으로 재정의 (Instance-level overriding)할 수 있다.
calc.add = lambda a, b: a * b # instance에 동적 할당
calc.add(2, 3) # 6 (재정의된 함수가 호출됨)
4.2 Special Method: Interpreter가 호출
class MyNumber:
def __init__(self, value):
self.value = value
def __add__(self, other):
return MyNumber(self.value + other.value)
a = MyNumber(10)
b = MyNumber(20)
c = a + b # interpreter가 __add__를 호출
Special method의 특징:
- Python interpreter가 호출한다.
- 일반 attribute lookup을 사용하지 않는다.
- Type(Class)에 정의된 것만 인식한다.
- Instance에 동적으로 재정의해도 무시된다.
다음의 예는 instance-level overriding이 영향을 주지 못함을 보여줌.
class MyLen:
def __len__(self):
return 10
obj = MyLen()
print(len(obj)) # 10
obj.__len__ = lambda: 999 # instance에 동적 할당
print(obj.__len__()) # 999 (직접 호출하면 작동)
print(len(obj)) # 10 (len()은 여전히 Class의 __len__ 사용!)
이것은 매우 중요한 차이이다:
Special method는
Instance가 아닌
Type(Class)을 기준으로 dispatch된다.
5. 왜 Type을 기준으로 하는가?
Python이 special method를 Type 기준으로 dispatch하는 이유는 일관성과 예측 가능성 때문이다.
만약 instance마다 __len__을 다르게 정의할 수 있다면,
같은 Class의 object들이 len()에 대해 완전히 다르게 동작할 수 있다. 이는 코드의 예측 가능성을 해친다.
다음 구조를 기억하자:
Instance ─── 상태(데이터)를 가짐
│
▼
Class ───── 동작(special method)을 정의함
│
▼
Interpreter ─ Class에 정의된 special method를 dispatch함
- Instance는 자신만의 데이터(
self.value등)를 가진다. - 하지만 연산자나 built-in 함수와의 상호작용 방식은 Class가 결정한다.
6. 모든 것은 Object이다
Python의 가장 중요한 특징 중 하나는 다음이다:
Python에서 모든 것은 Object이다.
Class조차도 Object이다.
class Dog:
pass
print(type(Dog)) # <class 'type'>
Dog이라는 Class 자체도 하나의 object이며, 그 type은 type이다.
6.1 계층 구조
class Animal:
pass
class Dog(Animal):
pass
buddy = Dog()
print(type(buddy)) # <class '__main__.Dog'>
print(type(Dog)) # <class 'type'>
print(type(Animal)) # <class 'type'>
print(type(type)) # <class 'type'>
이 구조에서:
buddy는Dog의 instanceDog은Animal의 subclass이면서,type의 instancetype은 모든 class의 type이며, 자기 자신의 type이기도 함 (self-hosted)
6.2 왜 이것이 중요한가?
앞서 배운 규칙을 떠올려보자:
Special method는
Type을 기준으로 dispatch된다.
이 규칙은 Instance뿐만 아니라 Class에도 동일하게 적용된다:
- Instance의 동작 → Instance의 Type인 Class가 정의
- Class의 동작 → Class의 Type인 Metaclass가 정의
Instance의 special method dispatch ← Class가 담당
Class의 special method dispatch ← Metaclass가 담당
7. Metaclass: Class를 만드는 Class
7.1 Metaclass란?
Metaclass는 Class를 생성하는 Class이다.
일반적으로 우리는 Class를 정의하고, 그 Class로 instance를 만든다:
class Dog: # class 정의
pass
buddy = Dog() # instance 생성
마찬가지로, Metaclass는 Class를 만든다:
# type이 Dog class를 만든다
Dog = type('Dog', (), {})
기본 Metaclass는 type이다.
7.2 class 문이 실행될 때 일어나는 일
class Dog:
species = "Canis familiaris"
def bark(self):
return "Woof!"
이 코드가 실행되면, Python은 내부적으로 다음과 유사한 과정을 거친다:
- Class body를 실행하여 namespace(dict)를 생성:
{"species": "Canis familiaris", "bark": <function>} - Metaclass(기본값
type)를 호출:type("Dog", (), namespace)클래스 = type('클래스이름', base 클래스 튜플, 속성 namespace)
- Class object가 생성됨
# 위 class 정의와 동등한 코드
def bark(self):
return "Woof!"
Dog = type("Dog", (), {"species": "Canis familiaris", "bark": bark})
7.3 Custom Metaclass 예제
Metaclass를 직접 정의하면 Class 생성 과정을 제어할 수 있다:
class AutoStr(type):
"""__str__이 없으면 자동으로 추가하는 metaclass"""
def __new__(cls, name, bases, namespace):
if "__str__" not in namespace:
def __str__(self):
return f"{name} instance"
namespace["__str__"] = __str__
return super().__new__(cls, name, bases, namespace)
class Person(metaclass=AutoStr):
def __init__(self, name):
self.name = name
p = Person("Alice")
print(p) # "Person instance" (__str__이 자동 추가됨)
이 예제에서:
AutoStrmetaclass가Personclass를 생성할 때__str__이 정의되어 있지 않으면 자동으로 추가한다.
Metaclass는
Data Model(special method)이 적용될 구조 자체를
설계하는 상위 계층이다.
참고: Attribute Lookup의 이해
지금까지 special method의 dispatch 규칙을 살펴보았다. 이제 일반적인 attribute access가 어떻게 동작하는지 알아보자.
8.1 Instance Attribute Access
obj.attr을 실행하면 Python은 다음 과정을 거친다:
Step 1: __getattribute__ 호출
모든 attribute access는 type(obj).__getattribute__(obj, 'attr')로 시작한다.
Step 2: 기본 __getattribute__의 탐색 순서
(a) Type의 MRO에서 data descriptor 탐색
→ 찾으면: descriptor.__get__(obj, type(obj)) 반환
(b) Instance의 __dict__ 탐색
→ 찾으면: 해당 값 반환
(c) Type의 MRO에서 non-data descriptor 또는 class attribute 탐색
→ 찾으면: 해당 값 반환
(d) 모두 실패: AttributeError 발생
주의할 점은 일반 method는 non-data descriptor임!
때문에 (a)에서 탐색되지 못하며,
(b)가 먼저 있어서 instance-level overriding이 가능함.
Step 3: __getattr__ fallback
앞서에서 AttributeError가 발생할 경우 한정하여,
만약 __getattr__이 정의되어 있다면 이를 호출 (fallback).
class Example:
x = 10 # class attribute
def __getattr__(self, name):
return f"'{name}' not found"
e = Example()
e.y = 20 # instance attribute
print(e.x) # 10 (class attribute)
print(e.y) # 20 (instance __dict__)
print(e.z) # "'z' not found" (__getattr__ fallback)
앞서 instance에서 일반 method의 호출을 결정하는 attribute lookup 도 내부를 살펴보면 Type 이 관여함.
둘 다 궁극적으로 Class(instace 의 Type)에 정의된 method를 사용하지만:
- 일반 method: obj.method() → Instance __dict__ 먼저 확인 → 없으면 Class에서 탐색
- Special method: len(obj) → Instance __dict__ 건너뛰고 → type(obj)에서 바로 탐색
8.2 Descriptor란?
Descriptor는 __get__, __set__, __delete__ 중 하나 이상을 정의한 object이다.
- Data descriptor:
__get__과 함께__set__또는__delete__정의 - Non-data descriptor:
__get__만 정의. 일반 method가 여기에 속함.
핵심: Data descriptor는 instance __dict__보다 우선순위가 높다.
class DataDesc:
def __get__(self, obj, objtype=None):
return "from data descriptor"
def __set__(self, obj, value):
pass # __set__ 정의 → data descriptor
class NonDataDesc:
def __get__(self, obj, objtype=None):
return "from non-data descriptor"
class MyClass:
data = DataDesc()
nondata = NonDataDesc()
obj = MyClass()
obj.__dict__['data'] = "from instance"
obj.__dict__['nondata'] = "from instance"
print(obj.data) # "from data descriptor" (data descriptor 우선)
print(obj.nondata) # "from instance" (instance __dict__ 우선)
앞서 애기한 일반 method는 non-descriptor에 속함. 다음 예제코드를 참고:
class MyClass:
def method(self): # non-data descriptor (function)
return "from class"
@property
def prop(self): # data descriptor
return "from class"
obj = MyClass()
obj.__dict__['method'] = lambda: "from instance"
obj.__dict__['prop'] = "from instance"
print(obj.method()) # "from instance" (b에서 찾음)
print(obj.prop) # "from class" (a에서 data descriptor가 우선)
8.3 Class Attribute Access
Class attribute access(Cls.attr)는 Metaclass가 관여한다.
앞서 배운 규칙이 동일하게 적용된다:
- Instance attribute resolution → Type(Class)이 관여
- Class attribute resolution → Type(Metaclass)이 관여
class Meta(type):
@property
def class_prop(cls):
return "from metaclass"
class MyClass(metaclass=Meta):
pass
print(MyClass.class_prop) # "from metaclass"
9. 전체 구조 정리
Python Data Model의 핵심을 정리하면 다음과 같다:
9.1 Special Method Dispatch
Python Syntax (예: a + b, len(obj))
│
▼
해당 Object의 Type에서 special method 탐색
│
▼
Type에 정의된 special method 호출
- Syntax는 special method로 변환되어 dispatch된다.
- Dispatch는 Instance가 아닌 Type을 기준으로 한다.
- Instance-level override는 무시된다.
9.2 객체 계층 구조
instance ──▶ class ──▶ metaclass
│ │ │
│ │ └─ class 생성 규칙 정의
│ └─ instance의 special method 정의
└─ 데이터(상태) 보유
- Instance는 Class의 instance이다.
- Class는 Metaclass의 instance이다.
- Metaclass는 기본적으로
type이다.
9.3 일반 Method vs Special Method
| 구분 | 일반 Method | Special Method |
| 호출 주체 | 사용자 코드 | Python Interpreter |
| Lookup 방식 | Attribute lookup | Type 기준 direct dispatch |
| Instance 재정의 | 가능 | 무시됨 |
| 예시 | obj.calculate() |
len(obj), obj + other |
요약
Python Data Model은 다음을 정의한다:
- Syntax와 Special Method의 매핑:
+,len(),[]같은 syntax가 어떤 special method로 변환되는지 - Dispatch 규칙: 해당 special method를 어디서(Type) 찾아 호출하는지
- 좀 더 정확히 애기하면 일반 method의 dispatch도 Python Data Model에서 정의하고 있음.
- 단, 일반 method는 attribute lookup 과정으로 dispatch 가 수행되고,
- sepcial method는 __getattribute__ 를 우회한다는 차이가 있을 뿐임.
- 객체 계층 구조: Instance → Class → Metaclass로 이어지는 Type 중심 구조
이 규칙을 이해하면:
- Custom class에서 연산자와 built-in 함수를 오버라이딩 할 수 있음.
- Python이 "왜 그렇게 동작하는지"를 설명할 수 있음.
- 고급 패턴(Descriptor, Metaclass)을 이해하고 활용할 수 있다.
Python Data Model은
Instance → Class → Metaclass로 이어지는 Type 중심 계층 위에서,
special method dispatch 규칙을 통해 객체의 동작을 정의하는 체계이다.
같이보면 좋은 자료들
2025.12.24 - [Python] - Python Class Definition and Object Model
Python Class Definition and Object Model
1. Object-Oriented Programming (OOP)에서의 Class 개념1.1 Class의 역할Class = State + Behavior Class는 Object-Oriented Programming에서 State와 Behavior를 함께 정의하는 추상화 단위임State는 Object가 보유하는 데이터의 집합
ds31x.tistory.com
열혈파이썬에서 해당 내용의 동영상 강좌:
https://player.vimeo.com/video/378703521
https://player.vimeo.com/video/378703708
https://player.vimeo.com/video/378703881
https://player.vimeo.com/video/378704294
'Python' 카테고리의 다른 글
| Hook 이란? (0) | 2026.02.23 |
|---|---|
| Python Class Definition and Object Model (0) | 2025.12.24 |
| PyPI에 wheel을 업로드하기 (0) | 2025.12.21 |
| 개발 디렉토리를 pip package로 설치하기 - pip install -e . (0) | 2025.12.21 |
| Typing: dynamic vs. static and strong vs. weak (0) | 2025.12.09 |