이터레이터와 제너레이터 이해하기 (파이썬 제너레이터 입문)
지난 글 끝에서 "근데 학생이 1000만 명이면 어떡하지?" 하고 슬쩍 흘렸잖아요. 사실 그게 오늘 글의 핵심이에요.
리스트에 1000만 명, 1억 명 데이터를 다 담으려고 하면 어떻게 될까요. 컴퓨터가 갑자기 느려지거나, 심하면 멈춰버리기도 해요. 메모리에 그 많은 걸 한꺼번에 올려두려니 당연한 일이죠.
그래서 파이썬에는 "필요할 때마다 하나씩 꺼내 쓰는" 방법이 따로 있어요. 그게 바로 오늘 이야기할 이터레이터랑 제너레이터예요.
고급 챕터 첫 글이라 살짝 긴장하셨을 수도 있는데, 비유로 풀면 의외로 쉬워요. 천천히 가볼게요.
리스트는 뷔페, 제너레이터는 분식집
리스트는 뷔페라고 생각하시면 돼요. 손님이 오기 전에 음식을 미리 다 차려놓는 거죠. 100가지 메뉴를 다 만들어서 진열대에 깔아두는 거예요. 바로 볼 수 있어서 좋긴 한데, 자리(메모리)를 엄청 많이 차지하잖아요.
제너레이터는 즉석조리 분식집이에요. 주문이 들어와야 떡볶이를 만들기 시작해요. 한 번에 한 접시만 만들면 되니까 부엌이 좁아도 괜찮고요.
이 한 가지 차이가 오늘 글 전체를 관통해요.
이터러블 vs 이터레이터: 책과 책갈피
용어 두 개가 비슷하게 생겨서 처음엔 진짜 헷갈리실 거예요. 저도 한참 같은 거라고 착각했거든요.
- 이터러블(iterable): for문에 넣을 수 있는 모든 것. 리스트, 튜플, 문자열, 딕셔너리 다 이터러블이에요.
- 이터레이터(iterator): 이터러블에서 뽑아낸 "지금 어디까지 봤는지 기억하는" 객체예요.
비유하자면 이터러블은 책 자체, 이터레이터는 그 책에 끼워둔 책갈피예요. 책갈피는 앞으로만 움직이고, 마지막 페이지에 닿으면 거기서 끝이에요.
for문이 사실 뒤에서 하는 일
nums = [1, 2, 3]
for n in nums:
print(n)
이렇게 익숙한 코드인데, 파이썬이 내부에서 진짜로 하는 일은 이거예요.
it = iter(nums) # 책에 책갈피 끼우기
while True:
try:
n = next(it) # 책갈피 다음 페이지로 한 칸
print(n)
except StopIteration: # 책이 끝나면 신호가 옴
break # 멈춤
for문은 마법이 아니었어요. iter()로 책갈피 만들고, next()로 한 칸씩 넘기고, 다 끝나면 StopIteration 받아서 멈추는 거예요. 이 세트가 python iterator의 전부입니다.
파이썬 yield: 함수를 일시정지하는 마법
여기부터가 진짜 재밌어요. 이터레이터를 직접 만들려면 코드가 좀 번거로운데, 파이썬은 훨씬 쉬운 길을 마련해뒀거든요. 바로 yield 키워드예요.
함수 안에 yield가 한 줄이라도 있으면, 그 함수는 자동으로 제너레이터 함수가 돼요.
return이랑 헷갈리실 텐데, 제일 큰 차이는 이거예요.
return은 함수가 퇴근하는 거고, yield는 잠깐 자리 비우는 거예요.
return을 만나면 함수는 그대로 끝나요. 근데 yield를 만나면 함수가 "잠깐만요" 하고 그 자리에 멈춰 있다가, 다시 부르면 그 자리부터 이어서 실행돼요. 변수 값이고 뭐고 다 그대로 유지된 채로요.
제너레이터 함수 직접 만들어보기
말로만 들으면 감이 안 오니까 직접 만들어볼게요.
def count_up(n):
i = 1
while i <= n:
yield i # 여기서 멈춤. 다음에 부르면 이 다음부터.
i += 1
gen = count_up(3) # 함수 호출인데도 아직 실행 안 됨
print(next(gen)) # 1 ← 첫 yield까지 실행
print(next(gen)) # 2 ← 멈춘 자리부터 다시
print(next(gen)) # 3
신기한 점 하나 짚을게요. count_up(3)을 호출했는데도 안에 있는 코드가 바로 돌아가지 않아요. 제너레이터 함수는 호출하는 순간 제너레이터 객체만 만들어줄 뿐이에요.
실제로 코드가 굴러가는 건 next()를 부른 순간부터예요.
물론 실전에서는 next()를 직접 부를 일은 거의 없어요. 그냥 for문에 넣으면 알아서 다 해줘요.
for n in count_up(5):
print(n)
# 1 2 3 4 5
제너레이터 표현식: 괄호만 바꾸면 끝
리스트 컴프리헨션 기억하시죠? 그거랑 거의 똑같이 생긴 친구가 있어요.
list_ver = [x*x for x in range(5)] # 리스트 → 메모리에 다 만듦
gen_ver = (x*x for x in range(5)) # 제너레이터 → 필요할 때 하나씩
print(list_ver) # [0, 1, 4, 9, 16]
print(gen_ver) # <generator object ...>
대괄호 []만 소괄호 ()로 바꿨을 뿐인데 동작이 완전히 달라져요. 참고로 ()는 튜플이 아니에요. 튜플 컴프리헨션 같은 건 없거든요. 소괄호는 제너레이터예요.
sum이나 max 같은 함수에 바로 넣을 땐 괄호도 생략할 수 있어요.
print(sum(x*x for x in range(5))) # 30
진짜 쓸모는 여기서 나와요
작은 예제만 보면 "그래서 뭐가 좋다는 거지?" 싶을 수 있어요. 진짜 위력은 데이터가 클 때 나와요.
# 1억 개의 제곱을 다루기 — 리스트로는 메모리 폭발
# squares = [x*x for x in range(100_000_000)] ← 이거 절대 하지 마세요
# 제너레이터: 괄호만 바꿨을 뿐인데 메모리 거의 안 씀
squares = (x*x for x in range(100_000_000))
print(sum(squares)) # 한 번에 하나씩만 처리하니까 OK
그리고 실무에서 진짜 많이 쓰는 패턴, 대용량 파일을 한 줄씩 읽는 거예요.
def read_big_file(path):
with open(path, encoding='utf-8') as f:
for line in f:
yield line.strip() # 한 줄 줬다가 멈춤
# 10GB 로그 파일이어도 메모리 부담 거의 없음
for line in read_big_file('huge.log'):
if 'ERROR' in line:
print(line)
10GB짜리 파일도 메모리 1GB짜리 노트북에서 처리할 수 있어요. 제너레이터 덕분에요.
자주 하는 실수 두 가지
처음에 거의 모든 분이 부딪히는 함정이 있어요.
첫 번째, 제너레이터는 일회용이에요.
gen = (x*x for x in range(5))
print(list(gen)) # [0, 1, 4, 9, 16]
print(list(gen)) # [] ← 어? 왜 비어요?
책갈피가 이미 마지막 페이지에 도달했으니까요. 다시 보고 싶으면 책갈피를 새로 끼워야 해요. 즉, 제너레이터를 다시 만들어야 해요.
두 번째, 큰 제너레이터에 list() 씌우면 의미가 없어요.
메모리 아끼려고 제너레이터 만들었는데, list(gen) 하는 순간 1억 개가 한꺼번에 메모리로 올라와요. 도루묵이죠. 굳이 리스트로 안 바꾸고 for문에 바로 넣어 쓰세요.
직접 해보기: 피보나치
오늘 배운 걸 다 녹인 예제예요.
def fibonacci():
a, b = 0, 1
while True: # 무한 루프인데도 괜찮아요
yield a
a, b = b, a + b
fib = fibonacci()
for _ in range(10):
print(next(fib), end=' ')
# 0 1 1 2 3 5 8 13 21 34
무한 수열인데도 메모리 걱정이 없어요. 필요한 만큼만 꺼내 쓰니까요. 무한 리필 음료라고 생각하시면 돼요.
과제: 위 피보나치를 이용해서 처음 20개의 피보나치 수 중 짝수만 출력해보세요. 힌트는 for문 + if문 조합이에요.
큰 데이터가 들어와도 이제 좀 덜 무서우시죠. 메모리 폭발 걱정 없이 한 줄씩, 하나씩 처리하면 되니까요.
다음 글에서는 좀 결이 다른 얘기를 해볼 거예요. 코드가 길어지고 함수가 많아지면, 이 매개변수가 뭘 받는 건지 헷갈리는 순간이 와요. 그걸 미리 표시해두는 방법이 있는데, 타입 힌팅이라고 해요. 고급은 어려운 게 아니라 코드를 더 단단하게 만드는 단계라는 걸 그때 더 느끼실 거예요.
제 글이 도움이 되셨다면 댓글 & 공감 부탁드려요 😀
'Application > Python' 카테고리의 다른 글
| [Python] 파이썬 비동기 프로그래밍, async와 await 기초부터 (0) | 2026.05.27 |
|---|---|
| [Python] 파이썬 타입 힌팅, 모르고 쓰면 손해예요 (0) | 2026.05.26 |
| [Python] 파이썬 성적 관리 프로그램 만들기 — 중급 종합 실습 (0) | 2026.05.20 |
| [Python] 파이썬 가상환경과 pip 완벽 정리 (Windows 기준) (0) | 2026.05.19 |
| [Python] 파이썬 데코레이터: 함수를 꾸며주는 마법 (@ 기호의 정체) (0) | 2026.05.15 |