-
Iterable ? Iterator? Generator?python 2026. 1. 9. 03:53
파이썬을 공부하다 보면 for 문을 수도 없이 사용하게 됩니다. 하지만 Iterable, Iterator, Generator라는 용어가 나오면 갑자기 머리가 복잡해지죠.
"리스트도 반복되고, 제너레이터도 반복되는데 도대체 무슨 차이지?"
오늘은 이 세 가지 개념의 정확한 정의와 포함 관계(상속 관계)를 코드로 확실하게 정리해 보겠습니다.
한 눈에 보는 포함 관계 (The Big Picture)
결론부터 말하면, 이들은 별개의 개념이 아니라 수학의 부분집합(Subset) 혹은 상속(Inheritance) 관계를 가집니다.
Iterable ⊃ Iterator ⊃ Generator
(이터러블이 가장 크고, 제너레이터가 가장 작습니다.)
- Iterable: 반복 가능한 모든 것 (가장 큰 범위)
- Iterator: 이터러블 중에서 next()로 값을 꺼낼 수 있는 것
- Generator: 이터레이터를 아주 쉽게 만들 수 있게 해주는 특수한 문법

이터러블 (Iterable)
- 정의: "멤버를 하나씩 차례로 반환할 수 있는 객체"
- 특징: for 문을 돌릴 수 있으면 모두 이터러블입니다.
- 종류: list, tuple, str, dict, set 등 우리가 아는 대부분의 자료형.
- 기술적 조건: 내부적으로 __iter__ 메소드를 가지고 있어야 합니다.
# 리스트는 이터러블입니다. my_list = [1, 2, 3] for i in my_list: print(i) # 하지만 next()는 쓸 수 없습니다. (아직 이터레이터가 아니니까요) # next(my_list) # TypeError 발생!비유: '책(Book)'. 읽을 수는 있지만, 내가 어디까지 읽었는지 스스로 기억하지는 못합니다.
이터레이터 (Iterator)
- 정의: "데이터의 스트림을 표현하는 객체"
- 특징: next() 함수를 호출할 때마다 다음 값을 리턴합니다.
- 기술적 조건: __iter__와 __next__ 메소드를 모두 가지고 있어야 합니다.
- 만드는 법: 이터러블에 iter() 함수를 씌우면 이터레이터가 됩니다.
my_list = [1, 2, 3] my_iter = iter(my_list) # 리스트를 이터레이터로 변환 print(type(my_iter)) # <class 'list_iterator'> print(next(my_iter)) # 1 print(next(my_iter)) # 2 print(next(my_iter)) # 3 # print(next(my_iter)) # StopIteration 에러 발생 (더 이상 값이 없음)비유: '북마크(Bookmark)'. 책(이터러블)에 꽂힌 북마크는 내가 어디를 읽을 차례인지 정확히 알고 있습니다.
제너레이터 (Generator)
- 정의: "이터레이터를 생성하는 아주 간편하고 강력한 도구"
- 특징: 함수 안에 return 대신 yield를 사용하거나, (x for x in data) 형태의 표현식을 사용합니다.
- 핵심: 모든 제너레이터는 이터레이터입니다. 따라서 next()를 쓸 수 있습니다.
- 장점: Lazy Evaluation(지연 평가). 데이터를 미리 메모리에 다 만들어두지 않고, 필요할 때마다 하나씩 생성하므로 메모리 효율이 압도적입니다.
# 1. 제너레이터 함수 (yield 사용) def my_gen_func(): yield 1 yield 2 yield 3 # 2. 제너레이터 표현식 (괄호 사용) my_gen_exp = (x for x in [1, 2, 3]) print(next(my_gen_exp)) # 1비유: '소설가'. 책처럼 이미 쓰여진 내용을 읽는 게 아니라, 독자가 "다음 내용 뭐야?"(next)라고 물어볼 때마다 즉석에서 다음 문장을 써서 건네줍니다.
코드로 증명하는 상속 관계 (팩트 체크)
파이썬의 추상 베이스 클래스(collections.abc)를 사용하면 이 족보를 눈으로 확인할 수 있습니다.
from collections.abc import Iterable, Iterator, Generator # 제너레이터 생성 gen = (x for x in range(3)) # 1. 제너레이터는 이터레이터인가? -> YES print(isinstance(gen, Iterator)) # True # 2. 제너레이터는 이터러블인가? -> YES print(isinstance(gen, Iterable)) # True # 3. 리스트는 이터레이터인가? -> NO (이터러블일 뿐) print(isinstance([1, 2, 3], Iterator)) # False
함수로 보는 Iterable의 위력
우리는 흔히 합계를 구할 때 sum([1, 2, 3])처럼 리스트를 넣습니다. 하지만 파이썬 공식 문서를 보면 sum()의 정의는 다음과 같습니다.
sum(iterable, start=0)
매개변수 이름이 list가 아니라 iterable입니다. 이게 무슨 뜻일까요?
"무엇이든 반복만 가능하면 된다" (다형성)
sum() 함수는 들어온 데이터가 리스트인지, 튜플인지, 세트인지, 아니면 제너레이터인지 신경 쓰지 않습니다. 그저 "하나씩 꺼낼 수 있는(Iterable) 녀석인가?"만 확인합니다.
내부적으로 sum()은 이렇게 동작한다고 상상해볼 수 있습니다. (Psudo 코드)
# Psudo Code def my_sum(iterable): total = 0 # 들어온 게 리스트든 제너레이터든 상관없이 반복문(for)만 돌릴 수 있으면 OK for x in iterable: total += x return total이 덕분에 우리는 다양한 자료형을 sum()에 넣을 수 있습니다.
- sum([1, 2, 3]) (리스트: 이터러블 O)
- sum((1, 2, 3)) (튜플: 이터러블 O)
- sum({1, 2, 3}) (세트: 이터러블 O)
- sum(range(1, 4)) (Range: 이터러블 O)
- sum(x for x in range(1, 4)) (제너레이터: 이터러블 O)
리스트 vs 제너레이터: 메모리 사용량의 결정적 차이
이 개념이 가장 빛을 발하는 순간이 바로 제너레이터를 sum()에 넣었을 때입니다.
만약 1억 개의 숫자를 더해야 한다면 어떻게 될까요?
- A. 리스트를 사용하는 경우 (비효율적)
# [1, 2, ..., 100000000] 리스트를 메모리에 통째로 만듦 # 메모리 폭발 가능성 있음 💥 result = sum([i for i in range(100000000)])- 제너레이터를 사용하는 경우 (효율적)
# 숫자를 미리 만들지 않음. # sum()이 "다음 숫자 줘" 할 때마다 하나씩 생성해서 더하고 버림. # 메모리는 숫자 1개 분량만 필요 result = sum(i for i in range(100000000))sum()이 이터러블을 인자로 받도록 설계되었기 때문에, 거대한 리스트를 만들지 않고도 스트림(Stream) 방식으로 데이터를 흘려보내며 계산할 수 있는 것입니다.
Iterator를 쓰면 이렇게 "하나씩 꺼내준다"는 점에서는 메모리를절약하는데, 하지만 메모리를 아끼느냐(Lazy Evaluation)"에 대해서는 원본이 무엇이냐에 따라 다릅니다.
# 1. 거대한 리스트 생성 (이미 메모리에 1억 개 데이터가 꽉 참 💥) big_list = [1, 2, 3, ... 1억] # 이터레이터로 변환 my_iter = iter(big_list) # 하나씩 꺼냄 print(next(my_iter)) # 2. 제너레이터 생성 (데이터 생성 안 함, 규칙만 기억함 🍃) my_gen = (x for x in range(100000000)) # 하나씩 꺼냄 (이때 데이터를 생성!) print(sys.getsizeof(next(my_gen))) #28 byte int객체크기리스트를 Iterator로 변환을 해도, 이미 1억개의 거대 메모리에서 하나씩 꺼내는 겁니다. 반면 my_gen은 next 시에 그때 데이타를 가져옵니다.
핵심 요약 (Cheat Sheet)
구분 Iterable (이터러블) Iterator (이터레이터) Generator (제너레이터) 대표 예시 리스트, 튜플, 문자열 iter(리스트) 반환값 yield 함수, (...) 표현식 for 문 사용 O O O next() 사용 X O O 메모리 효율 데이터 크기만큼 차지 상태만 기억하므로 작음 최상 (필요할 때 생성) 포함 관계 할아버지 아버지 자식 결론
- Iterable: 반복할 수 있는 재료 (리스트 등)
- Iterator: 재료를 하나씩 꺼내주는 도구 (next() 보유)
- Generator: 그 도구를 아주 쉽게 만드는 방법 (yield 사용)
우리가 평소에 쓰는 for i in list: 구문은 사실 파이썬 내부적으로 list(이터러블)를 iterator로 변환한 뒤, next()를 계속 호출하다가 StopIteration 에러가 나면 멈추는 방식으로 동작합니다.
이 관계를 이해하면 대용량 데이터를 처리할 때 왜 리스트 대신 제너레이터를 써야 하는지(Memory Efficiency) 명확히 알 수 있습니다.
'python' 카테고리의 다른 글
모듈, 패키지, 라이브러리, 그리고 프레임워크 (1) 2026.01.13 가변 인자와 Packing/Unpacking의 원리 (0) 2026.01.13 제너레이터(Generator) 활용 예제 (0) 2026.01.11 Closure, 도대체 왜 쓰는 걸까? (0) 2026.01.09