
28. 매직 메서드 - 파이썬 객체의 비밀
print()로 객체를 출력하면 왜 이상한 주소가 나올까?+연산자를 내 클래스에 쓸 수 있을까? 밑줄 두 개로 감싸인 매직 메서드(던더 메서드)의 비밀을 풀어보자.
매직 메서드란?
매직 메서드(Magic Method)는 __이름__ 형태로, 파이썬이 특정 상황에서 자동으로 호출하는 특별한 메서드다. 던더(dunder) 메서드라고도 부른다 (double underscore).
# 사실 이미 써왔다!
len([1, 2, 3]) # → [1, 2, 3].__len__()
"hello" + " world" # → "hello".__add__(" world")
3 + 5 # → (3).__add__(5)
우리가 자연스럽게 쓰는 파이썬 문법 대부분이 내부적으로 매직 메서드를 호출하고 있다.

str과 repr - 객체 표현
str - 사용자를 위한 문자열
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
# __str__ 없이 출력하면?
dog = Dog("멍멍이", 3)
print(dog) # <__main__.Dog object at 0x...> ← 쓸모없다!
__str__을 정의하면 print()와 str()에서 사용된다:
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name} ({self.age}살)"
dog = Dog("멍멍이", 3)
print(dog) # 멍멍이 (3살)
print(str(dog)) # 멍멍이 (3살)
repr - 개발자를 위한 표현
__repr__은 주로 디버깅용이다. 객체를 재생성할 수 있는 표현을 반환하는 것이 관례다.
class Dog:
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f"{self.name} ({self.age}살)"
def __repr__(self):
return f"Dog('{self.name}', {self.age})"
dog = Dog("멍멍이", 3)
print(str(dog)) # 멍멍이 (3살) ← __str__
print(repr(dog)) # Dog('멍멍이', 3) ← __repr__
# 리스트 안에서는 __repr__이 사용된다
dogs = [Dog("멍멍이", 3), Dog("초코", 5)]
print(dogs) # [Dog('멍멍이', 3), Dog('초코', 5)]
팁:
__str__이 없으면__repr__이 대신 사용된다. 둘 중 하나만 구현한다면__repr__을 추천한다.
len - 길이
len() 함수가 호출될 때 실행된다.
class Playlist:
def __init__(self, name):
self.name = name
self.songs = []
def add(self, song):
self.songs.append(song)
def __len__(self):
return len(self.songs)
def __str__(self):
return f"플레이리스트 '{self.name}' ({len(self)}곡)"
playlist = Playlist("내 즐겨찾기")
playlist.add("좋은 날 - 아이유")
playlist.add("봄날 - BTS")
playlist.add("밤편지 - 아이유")
print(len(playlist)) # 3
print(playlist) # 플레이리스트 '내 즐겨찾기' (3곡)
eq과 lt - 비교 연산자
eq - 같은지 비교 (==)
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
return self.x == other.x and self.y == other.y
p1 = Point(1, 2)
p2 = Point(1, 2)
p3 = Point(3, 4)
print(p1 == p2) # True (좌표가 같으므로)
print(p1 == p3) # False
lt - 작은지 비교 (<)
class Student:
def __init__(self, name, score):
self.name = name
self.score = score
def __lt__(self, other):
return self.score < other.score
def __repr__(self):
return f"{self.name}({self.score})"
students = [
Student("김철수", 85),
Student("이영희", 92),
Student("박민수", 78),
]
# __lt__가 있으면 sorted()와 sort()를 쓸 수 있다!
sorted_students = sorted(students)
print(sorted_students) # [박민수(78), 김철수(85), 이영희(92)]
비교 매직 메서드 종류
| 메서드 | 연산자 |
|---|---|
__eq__ |
== |
__ne__ |
!= |
__lt__ |
< |
__le__ |
<= |
__gt__ |
> |
__ge__ |
>= |
add - 더하기 연산자
class Money:
def __init__(self, amount, currency="원"):
self.amount = amount
self.currency = currency
def __add__(self, other):
if self.currency == other.currency:
return Money(self.amount + other.amount, self.currency)
raise ValueError("통화가 다릅니다!")
def __str__(self):
return f"{self.amount:,}{self.currency}"
m1 = Money(50000)
m2 = Money(30000)
m3 = m1 + m2 # __add__ 호출
print(m3) # 80,000원
산술 매직 메서드 종류
| 메서드 | 연산자 |
|---|---|
__add__ |
+ |
__sub__ |
- |
__mul__ |
* |
__truediv__ |
/ |
__floordiv__ |
// |
__mod__ |
% |
__pow__ |
** |
getitem - 인덱싱과 슬라이싱
[] 연산자를 사용할 수 있게 해준다.
class Deck:
def __init__(self):
suits = ["스페이드", "하트", "다이아", "클로버"]
ranks = ["A"] + [str(n) for n in range(2, 11)] + ["J", "Q", "K"]
self.cards = [f"{s} {r}" for s in suits for r in ranks]
def __len__(self):
return len(self.cards)
def __getitem__(self, index):
return self.cards[index]
deck = Deck()
print(len(deck)) # 52
print(deck[0]) # 스페이드 A
print(deck[-1]) # 클로버 K
print(deck[0:3]) # ['스페이드 A', '스페이드 2', '스페이드 3']
# for 문도 자동으로 동작한다!
for card in deck[:5]:
print(card)
실전 예시: Vector 클래스
모든 매직 메서드를 활용한 완성도 높은 예시를 만들어보자.
class Vector:
"""2D 벡터 클래스"""
def __init__(self, x, y):
self.x = x
self.y = y
# 문자열 표현
def __str__(self):
return f"Vector({self.x}, {self.y})"
def __repr__(self):
return f"Vector({self.x}, {self.y})"
# 산술 연산
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other):
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
# 비교 연산
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __lt__(self, other):
return self.magnitude() < other.magnitude()
# 길이 (크기)
def __len__(self):
return 2 # 2D 벡터이므로 항상 2
def __abs__(self):
return self.magnitude()
# 인덱싱
def __getitem__(self, index):
if index == 0: return self.x
elif index == 1: return self.y
else: raise IndexError("Vector index out of range")
# 크기 (유틸리티)
def magnitude(self):
return (self.x ** 2 + self.y ** 2) ** 0.5
# 사용해보기
v1 = Vector(3, 4)
v2 = Vector(1, 2)
# 문자열
print(v1) # Vector(3, 4)
# 산술
print(v1 + v2) # Vector(4, 6)
print(v1 - v2) # Vector(2, 2)
print(v1 * 3) # Vector(9, 12)
# 비교
print(v1 == Vector(3, 4)) # True
print(v1 < v2) # False (v1이 더 큼)
# 크기
print(abs(v1)) # 5.0
# 인덱싱
print(v1[0]) # 3
print(v1[1]) # 4
# 정렬도 가능!
vectors = [Vector(3, 4), Vector(1, 1), Vector(2, 3)]
print(sorted(vectors))
# [Vector(1, 1), Vector(2, 3), Vector(3, 4)]
bool과 contains
bool - 참/거짓 판단
class ShoppingCart:
def __init__(self):
self.items = []
def add(self, item):
self.items.append(item)
def __bool__(self):
return len(self.items) > 0
def __len__(self):
return len(self.items)
cart = ShoppingCart()
print(bool(cart)) # False (비어있음)
if not cart:
print("장바구니가 비었습니다!")
cart.add("파이썬 책")
print(bool(cart)) # True
if cart:
print("장바구니에 상품이 있습니다!")
contains - in 연산자
class Team:
def __init__(self, name):
self.name = name
self.members = []
def add(self, member):
self.members.append(member)
def __contains__(self, member):
return member in self.members
team = Team("파이썬팀")
team.add("김철수")
team.add("이영희")
print("김철수" in team) # True
print("박민수" in team) # False
직접 해보기
문제 1. Temperature 클래스를 만들어보자. 섭씨 온도를 저장하고, __str__으로 "25.0°C" 형태로 출력하고, __add__로 두 온도를 더할 수 있게 하자. __lt__로 비교도 가능하게 하자.
문제 2. Stack 클래스를 만들어보자. push(), pop() 메서드와 함께 __len__, __bool__, __str__, __contains__ 매직 메서드를 구현하자.
문제 3. Fraction (분수) 클래스를 만들어보자. 분자(numerator)와 분모(denominator)를 가지고, __add__, __mul__, __str__, __eq__를 구현하자.
문제 4. Matrix2x2 클래스를 만들어보자. 2x2 행렬을 표현하고, __add__(행렬 덧셈), __mul__(스칼라 곱), __str__(보기 좋은 출력), __getitem__(인덱싱)을 구현하자.
정답 보기
# 문제 1 - Temperature
class Temperature:
def __init__(self, celsius):
self.celsius = celsius
def __str__(self):
return f"{self.celsius}°C"
def __add__(self, other):
return Temperature(self.celsius + other.celsius)
def __lt__(self, other):
return self.celsius < other.celsius
def __eq__(self, other):
return self.celsius == other.celsius
t1 = Temperature(25)
t2 = Temperature(30)
print(t1) # 25°C
print(t1 + t2) # 55°C
print(t1 < t2) # True
print(sorted([Temperature(30), Temperature(10), Temperature(25)]))
# 문제 2 - Stack
class Stack:
def __init__(self):
self.items = []
def push(self, item):
self.items.append(item)
def pop(self):
if self.items:
return self.items.pop()
raise IndexError("빈 스택입니다")
def __len__(self):
return len(self.items)
def __bool__(self):
return len(self.items) > 0
def __str__(self):
return f"Stack({self.items})"
def __contains__(self, item):
return item in self.items
s = Stack()
s.push(1)
s.push(2)
s.push(3)
print(s) # Stack([1, 2, 3])
print(len(s)) # 3
print(2 in s) # True
print(s.pop()) # 3
# 문제 3 - Fraction (분수)
class Fraction:
def __init__(self, num, den):
self.num = num
self.den = den
self._simplify()
def _gcd(self, a, b):
while b:
a, b = b, a % b
return a
def _simplify(self):
g = self._gcd(abs(self.num), abs(self.den))
self.num //= g
self.den //= g
def __add__(self, other):
new_num = self.num * other.den + other.num * self.den
new_den = self.den * other.den
return Fraction(new_num, new_den)
def __mul__(self, other):
return Fraction(self.num * other.num, self.den * other.den)
def __eq__(self, other):
return self.num == other.num and self.den == other.den
def __str__(self):
return f"{self.num}/{self.den}"
f1 = Fraction(1, 3)
f2 = Fraction(2, 3)
print(f1 + f2) # 1/1
print(Fraction(1, 2) + Fraction(1, 4)) # 3/4
print(Fraction(2, 3) * Fraction(3, 4)) # 1/2
# 문제 4 - Matrix2x2
class Matrix2x2:
def __init__(self, a, b, c, d):
self.data = [[a, b], [c, d]]
def __add__(self, other):
return Matrix2x2(
self.data[0][0] + other.data[0][0],
self.data[0][1] + other.data[0][1],
self.data[1][0] + other.data[1][0],
self.data[1][1] + other.data[1][1]
)
def __mul__(self, scalar):
return Matrix2x2(
self.data[0][0] * scalar,
self.data[0][1] * scalar,
self.data[1][0] * scalar,
self.data[1][1] * scalar
)
def __getitem__(self, index):
row, col = index
return self.data[row][col]
def __str__(self):
return f"| {self.data[0][0]:4} {self.data[0][1]:4} |\n| {self.data[1][0]:4} {self.data[1][1]:4} |"
m1 = Matrix2x2(1, 2, 3, 4)
m2 = Matrix2x2(5, 6, 7, 8)
print(m1 + m2)
# | 6 8 |
# | 10 12 |
print(m1 * 3)
# | 3 6 |
# | 9 12 |
print(m1[0, 1]) # 2
오늘의 정리
| 매직 메서드 | 용도 |
|---|---|
| __str__ | print(), str() — 사용자용 문자열 |
| __repr__ | repr() — 개발자용/디버깅 표현 |
| __len__ | len() — 길이/크기 반환 |
| __eq__, __lt__ | ==, < 등 비교 연산자 |
| __add__, __sub__ | +, - 등 산술 연산자 |
| __getitem__ | obj[index] — 인덱싱/슬라이싱 |
| __bool__ | bool(), if obj: — 참/거짓 판단 |
| __contains__ | in 연산자 |
다음 편 예고: 데코레이터 - 함수를 꾸미는 함수
함수를 감싸서 기능을 추가하는 데코레이터! @ 기호의 비밀과 실전 활용법을 알아보자.
태그: 파이썬 Python 파이썬독학 매직메서드 던더메서드 __str__ __repr__ 연산자오버로딩 OOP 파이썬중급 IT교육
'Python' 카테고리의 다른 글
| API 활용하기 - 다른 서비스와 대화하기 (0) | 2026.02.23 |
|---|---|
| 웹 스크래핑 - 인터넷에서 데이터 수집하기 (0) | 2026.02.23 |
| 27. 클래스와 객체 (2) - 상속과 다형성 (0) | 2026.02.22 |
| 26. 클래스와 객체 (1) - 나만의 자료형 만들기 (0) | 2026.02.22 |
| 25. 가상환경과 프로젝트 관리 - 프로처럼 세팅하기 (1) | 2026.02.22 |
댓글