매직 메서드(magic method)는 파이썬에서 특정 문법이나 연산이 실행될 때 자동으로 호출되는 특별한 메서드로, __len__, __str__, __getitem__처럼 양쪽에 밑줄(__)이 붙는 형태를 가진다. 이 메서드들은 클래스에 구현하면 len(obj), print(obj), obj[0], obj + obj 같은 파이썬의 기본 문법과 자연스럽게 연결되어 객체의 동작을 직접 정의할 수 있다. 즉, 매직 메서드는 객체를 리스트처럼 사용하거나, 연산을 수행하거나, 출력 형태를 바꾸는 등 파이썬의 문법과 객체를 이어주는 핵심 기능이다.
매직 메서드의 사용이유
파이썬에는 이미 훌륭한 내장 타입과 함수들이 있지만, 기존 내장 타입의 기능만으로는 내가 원하는 규칙이나 비즈니스 로직을 표현할 수 없기 때문에 매직 메서드를 사용한다. 내장 함수의 편리함은 그대로 유지하면서, 그 안에 나만의 '커스텀 규칙'을 넣고 싶을 때 매직 메서드를 사용해 클래스를 만든다.
객체 출력
1. __str__()
__str__()는 객체를 사람이 읽기 쉬운 문자열로 표현하기 위해 사용하는 매직 메서드로, print(obj)나 str(obj)가 호출될 때 자동으로 실행된다. 이 메서드를 클래스에 정의하면 객체를 출력할 때 기본 주소값 대신 원하는 형태로 내용을 보여줄 수 있어 디버깅이나 사용자 출력에 매우 유용하다.
class Fruit:
def __init__(self, name, quantity):
self.name = name
self.quantity = quantity
def __str__(self):
return f"{self.name} {self.quantity}개" # str() 함수와 연결되어있는 매직 메서드
apple = Fruit('사과', 10)
print(apple) # print(str(apple))이 생략되어있다.
# 사과 10개
2. __repr__()
__repr__()은 객체를 개발자 관점에서 정확하게 표현하는 문자열을 반환하는 매직 메서드로, 주로 디버깅이나 로그에서 사용된다. repr(obj)를 호출하거나 인터프리터에서 객체를 직접 평가할 때 자동으로 호출되며, 가능하다면 객체를 다시 생성할 수 있는 형태의 문자열을 반환하는 것이 권장된다. __str__()이 사람이 읽기 좋은 출력이라면, __repr__()은 객체의 구조와 상태를 명확하게 보여주는 공식적인 표현이다.
class Fruit:
def __init__(self, name, quantity):
self.name = name
self.quantity = quantity
def __str__(self):
return f"{self.name} {self.quantity}개"
def __repr__(self):
return f"Fruit(name='{self.name}', quantity={self.quantity})" # 객체를 만드는 형태의 문자열
apple = Fruit('사과', 10)
print(repr(apple)) # __repr__ 호출
print(apple) # __str__ 없으면 __repr__ 호출 (같이있을때는 __str__이 우선시 됨)
# Fruit(name='사과', quantity=10)
# 사과 10개
제너레이터
제너레이터(generator)는 yield 키워드를 사용해 값을 한 번에 하나씩 생성해 반환하는 반복 객체로, 함수 형태로 간단하게 이터레이터를 만들 수 있는 파이썬의 기능이다. 일반 함수처럼 보이지만 yield를 만나면 값을 반환하면서도 실행 상태를 유지하고, 다음 호출 시 이어서 실행되기 때문에 전체 데이터를 한 번에 만들지 않고 필요할 때마다 생성할 수 있어 메모리 효율이 매우 높다.
class Counter:
def __init__(self, max):
self.max = max
self.current = 0
def __iter__(self):
return self # 자기 자신을 iterator로 사용
def __next__(self):
if self.current < self.max:
self.current += 1
return self.current
else:
raise StopIteration
counter = Counter(3)
# for문: iter(), next()함수를 자동으로 호출, StopIteration으로 멈춤
for num in counter:
print(num)
# 1
# 2
# 3
👉 for문이 내부적으로 자동으로 iter()를 호출
👉 이터레이터는 이터러블로부터 실제로 값을 하나씩 꺼내는 객체로, __iter__()와 __next__() 메서드를 모두 구현.
👉 __iter__()는 반복을 시작하게 하고, __next__()는 값을 하나씩 꺼내며, 더 이상 반환할 값이 없으면 StopIteration 예외를 발생시켜 반복을 종료.
def counter(max):
current = 0
while current < max:
current += 1
yield current
for num in counter(3):
print(num)
👉 yield의 의미
- 값을 반환한다.
- 함수 상태를 저장한다.
- 다음 호출 시 이어서 실행한다.
👉 제너레이터는 자동으로 __iter__ + __next__를 만들어준다.
인덱싱
1. __getitem__()
__getitem__()은 객체에서 인덱스나 키를 이용해 값을 조회할 때 호출되는 매직 메서드로, obj[index] 또는 obj[key] 형태로 접근하면 자동으로 실행된다. 이 메서드를 구현하면 사용자 정의 객체도 리스트나 딕셔너리처럼 데이터를 꺼낼 수 있으며, 내부적으로는 전달된 인덱스나 키를 기반으로 값을 반환하도록 작성하면 된다.
2. __setitem__()
__setitem__()은 객체에서 인덱스나 키를 이용해 값을 할당할 때 호출되는 매직 메서드로, obj[index] = value 형태의 코드가 실행되면 자동으로 호출된다. 이 메서드를 구현하면 사용자 정의 객체에 값을 저장하거나 수정하는 동작을 제어할 수 있으며, 전달된 인덱스와 값을 이용해 내부 데이터를 업데이트 하도록 구현한다.
3. __delitem__()
__delitem__()은 객체에서 인덱스나 키를 이용해 값을 삭제할 때 호출되는 매직 메서드로, del obj[index] 형태의 코드가 실행되면 자동으로 호출된다. 이 메서드를 구현하면 사용자 정의 객체에서 특정 데이터를 삭제하는 로직을 정의할 수 있으며, 전달된 인덱스를 기반으로 내부 데이터를 제거하도록 작성한다.
class MyList:
def __init__(self):
self.data = []
def __getitem__(self, index):
return self.data[index]
def __setitem__(self, index, value):
self.data[index] = value
def __delitem__(self, index):
del self.data[index]
ml = MyList()
ml.data = [10, 20, 30]
print(ml[0]) # __getitem__ -> 10
ml[1] = 99 # __setitem__
print(ml.data) # [10, 99, 30]
del ml[0] # __delitem__
print(ml.data) # [99, 30]
# 10
# [10, 99, 30]
# [99, 30]
객체를 함수, 값처럼 사용
1. __call__()
__call__()은 객체를 함수처럼 호출할 수 있게 만들어주는 매직 매서드로, obj() 형태로 객체를 실행하면 자동으로 호출된다. 이 메서드를 구현하면 클래스 인스턴스가 일반 함수처럼 동작할 수 있으며, 내부 데이터의 개수를 반환하도록 작성하면 된다.
class Multiplier:
def __init__(self, n):
self.n = n
def __call__(self, x):
return x * self.n
m = Multiplier(3)
print(m(10)) # 30
# 30
2. __len__()
__len__()은 객체의 길이를 정의하는 매직 메서드로, len(obj)를 호출할 때 자동으로 실행된다. 이 메서드를 구현하면 사용자 정의 객체도 리스트나 문자열처럼 길이를 가질 수 있으며, 내부 데이터의 개수를 반환하도록 작성하면 된다.
class Basket:
def __init__(self):
self.items1 = []
self.items2 = []
def __len__(self):
return len(self.items1) # __len__이 없이 print(len(b))를 출력하고자 하면 items1과 items2중 무엇인지 모르니 오류발생
b = Basket()
b.items1 = ["사과", "바나나", "오렌지"]
b.items2 = ["메론", "체리"]
print(len(b))
# 3
with 문
with 문은 파일, 네트워크, DB 연결처럼 사용 후 반드시 정리(clean-up)가 필요한 자원을 안전하게 관리하기 위한 문법으로, 블록이 시작될 때 필요한 작업을 수행하고 종료될 때 자동으로 정리 작업을 실행해준다. 이를 통해 try-finally를 직접 작성하지 않아도 되고, 예외가 발생하더라도 자원이 정상적으로 해제되도록 보장할 수 있다.
1. __enter__()
__enter__()는 with 문에 진입할 때 자동으로 호출되는 매직 메서드로, 자원을 준비하거나 초기화하는 역할을 한다. 이 메서드에서 반환한 값은 as 뒤에 변수에 할당되어 with 블록 내부에서 사용된다.
2. __exit__()
__exit__()는 with 블록이 끝날 때 자동으로 호출되는 매직 메서드로, 사용한 자원을 정리(clean-up)하는 역할을 한다. 이 메서드는 예외 발생 여부와 관계없이 반드시 실행되며, 예외가 발생한 경우에는 예외 타입, 값, traceback 정보를 인자로 받아 처리할 수 있다.
class MyResource:
def __enter__(self):
print("자원 열기")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("자원 정리")
with MyResource() as res:
print("자원 사용 중")
# 자원 열기
# 자원 사용 중
# 자원 정리
class MyResource:
def __enter__(self):
print("자원 열기")
return self
def __exit__(self, exc_type, exc_value, traceback):
print("자원 정리")
print("예외 타입:", exc_type)
with MyResource():
print("작업 중")
# 1 / 0 # 에러 발생
# 👉 에러가 나도 __exit__()는 반드시 실행됩니다.
# 자원 열기
# 작업 중
# 자원 정리
# 예외 타입: None'Python' 카테고리의 다른 글
| 모듈 (0) | 2026.06.01 |
|---|---|
| 예외처리 (0) | 2026.05.29 |
| 객체지향 프로그램 4대 패러다임 (1) | 2026.05.28 |
| 객체지향 프로그래밍 (0) | 2026.05.28 |
| 사용자 정의 함수 part2 (0) | 2026.05.28 |