Why Not SW CAMP 5기/수업 기록
[2월 2주차-2/12(1)]이터레이터(Iterator)와 제너레이터(Generator) 쉽게 이해하기
rubii
2025. 2. 12. 10:21
Python에서 이터레이터(iterator) 와 제너레이터(generator) 는 반복(iteration)을 효율적으로 처리하는 방식입니다.
1️⃣ 이터레이터(Iterator)란?
이터레이터는 한 번에 한 개의 요소만 가져올 수 있는 객체입니다.
보통 for 문을 사용할 때 자동으로 동작하는데, 직접 만들 수도 있습니다.
✅ 이터레이터의 특징
- 한 번 사용한 값은 사라지고 다시 돌아갈 수 없음
- for 문에서 자동으로 next()를 호출해 다음 값을 가져옴
- iter()로 이터레이터 객체를 만들고, next()로 값을 하나씩 꺼낼 수 있음
class MyIterator:
def __init__(self, data):
self.data = data
self.position = 0 # 오타 수정 (positon → position)
def __iter__(self):
return self
def __next__(self):
if self.position >= len(self.data):
raise StopIteration
result = self.data[self.position] # 오타 수정 (ppsition → position)
self.position += 1
return result
if __name__ == '__main__':
i = MyIterator([1, 2, 3]) # 리스트를 괄호가 아닌 대괄호로 감싸야 함
for item in i:
print(item)
🔹 리스트와 이터레이터 비교
numbers = [1, 2, 3, 4, 5] # 리스트
iterator = iter(numbers) # 리스트를 이터레이터로 변환
print(next(iterator)) # 1
print(next(iterator)) # 2
print(next(iterator)) # 3
📌 next(iterator) 를 호출할 때마다 한 개씩 꺼냄
📌 값이 없는데 계속 next()를 호출하면 StopIteration 오류 발생
2️⃣ 제너레이터(Generator)란?
제너레이터는 이터레이터를 더 쉽게 만들 수 있도록 도와주는 특수한 함수입니다.
return 대신 yield를 사용해 한 번에 하나씩 값을 반환합니다.
✅ 제너레이터의 특징
- 메모리 효율적 (한 번에 한 개의 값만 생성해서 저장 X)
- next()를 호출할 때마다 실행이 멈췄던 지점부터 다시 실행됨
- 일반 함수처럼 def 키워드로 만들지만, yield를 사용함
🔹 제너레이터 예제
def my_generator():
print("첫 번째 값 생성")
yield 1
print("두 번째 값 생성")
yield 2
print("세 번째 값 생성")
yield 3
gen = my_generator() # 제너레이터 객체 생성
print(next(gen)) # "첫 번째 값 생성" 출력 후 1 반환
print(next(gen)) # "두 번째 값 생성" 출력 후 2 반환
print(next(gen)) # "세 번째 값 생성" 출력 후 3 반환
📌 yield가 값을 반환한 후 다음 next()가 호출될 때 그 다음 코드부터 실행됨
4️⃣ 제너레이터의 실용 예제
🔹 큰 데이터 처리 (메모리 절약)
예를 들어, 1억 개의 숫자를 담는 리스트는 메모리를 많이 차지하지만, 제너레이터를 사용하면 필요할 때만 값을 생성합니다.
def count_up_to(max_num):
num = 1
while num <= max_num:
yield num
num += 1
gen = count_up_to(5) # 1부터 5까지 생성하는 제너레이터
for num in gen:
print(num) # 1, 2, 3, 4, 5 출력
📌 한꺼번에 모든 숫자를 저장하지 않고 필요할 때마다 값을 생성
🔹 제너레이터를 활용한 '시간이 오래 걸리는 작업' 처리
import time
def longtime_job():
print('작업 수행 중...')
time.sleep(1)
return '완료'
# 리스트 컴프리헨션 (즉시 실행)
list_job = [longtime_job() for i in range(5)] # 여기서 모든 작업이 실행됨
print(list_job[0]) # '완료' (이미 수행된 결과 출력)
📌 리스트 컴프리헨션([])은 모든 작업을 즉시 실행함 → 메모리 낭비 발생
🔹 제너레이터를 활용한 '지연 실행' (Lazy Evaluation)
list_job = (longtime_job() for i in range(5)) # 리스트가 아니라 제너레이터 객체 생성
print(next(list_job)) # 첫 번째 작업 수행 (그때 실행됨)
print(next(list_job)) # 두 번째 작업 수행
📌 제너레이터 표현식(())을 사용하면 필요한 순간에만 실행됨 → 메모리 절약 가능
5️⃣ 언제 사용하면 좋을까?
✔ 이터레이터
- 리스트, 튜플, 딕셔너리 같은 반복 가능한 객체를 직접 제어하고 싶을 때
- 반복할 데이터가 이미 정해져 있을 때
✔ 제너레이터
- 대량의 데이터를 효율적으로 처리할 때 (예: 로그 파일 읽기, 데이터 스트림 처리)
- 메모리를 절약하고 싶을 때 (예: 무한 숫자 생성, 대용량 데이터 처리)
💡 결론
이터레이터 제너레이터
구현 방식 | __iter__()와 __next__() 메서드를 구현 | yield를 사용하여 함수 형태로 구현 |
사용 방식 | 클래스로 직접 구현해야 함 | 함수로 간단히 구현 가능 |
메모리 효율 | 모든 데이터를 메모리에 저장 | 필요할 때만 데이터를 생성 (메모리 절약) |
재사용성 | 한 번 반복 후 재사용 불가 (새로운 객체 필요) | yield를 사용하여 중단된 부분부터 실행 가능 |
예제 | for 문에서 iter()와 next()를 직접 구현할 때 | 데이터 스트림, 대량 파일 읽기, API 요청 등에서 유용 |
💡 이터레이터는 반복 가능한 객체를 만들 때 직접 구현하는 경우 사용되며,
💡 제너레이터는 큰 데이터를 처리하거나 무거운 작업을 효율적으로 실행할 때 매우 유용합니다. 🚀