Python Generator vs Iterator 차이점 완벽 비교
Python의 generator와 iterator의 차이점, 각각의 사용 사례, 성능 비교를 통해 올바른 선택 방법을 알아봅니다.
Python Generator vs Iterator 차이점 완벽 비교
Python에서 데이터를 순회하는 두 가지 주요 방법: iterator와 generator. 비슷해 보이지만 각각의 특성과 사용 사례가 다릅니다.
Environment
$ python --version
Python 3.11.5
$ python -c "import sys; print(f'System memory: {sys.getsizeof([])} bytes for empty list')"
System memory: 56 bytes for empty listProblem: 메모리 효율성 문제
큰 데이터셋을 처리할 때 iterator와 generator의 차이를 이해하지 못하면 메모리 문제가 발생할 수 있습니다:
import sys
# List comprehension - 모든 데이터를 메모리에 로드
def get_squares_list(n):
return [x * x for x in range(n)]
# Generator expression - 필요할 때마다 생성
def get_squares_generator(n):
return (x * x for x in range(n))
# Compare memory usage
list_data = get_squares_list(1000000)
gen_data = get_squares_generator(1000000)
print(f"List size: {sys.getsizeof(list_data)} bytes")
print(f"Generator size: {sys.getsizeof(gen_data)} bytes")출력:
List size: 8448728 bytes
Generator size: 200 bytes메모리 사용량이 42,000배 이상 차이가 납니다.
Analysis: Iterator Protocol 이해
Iterator란?
Iterator는 __iter__와 __next__ 메서드를 가진 객체입니다:
class CountUp:
def __init__(self, start, end):
self.current = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.current >= self.end:
raise StopIteration
value = self.current
self.current += 1
return value
# Usage
counter = CountUp(1, 5)
for num in counter:
print(num, end=" ")
# Output: 1 2 3 4Generator란?
Generator는 iterator를 간단하게 만드는 문법입니다:
def count_up(start, end):
current = start
while current < end:
yield current
current += 1
# Usage - same behavior as iterator class
for num in count_up(1, 5):
print(num, end=" ")
# Output: 1 2 3 4Solution: Iterator와 Generator 비교
Iterator 구현
class FibonacciIterator:
def __init__(self, max_count):
self.max_count = max_count
self.count = 0
self.a = 0
self.b = 1
def __iter__(self):
return self
def __next__(self):
if self.count >= self.max_count:
raise StopIteration
value = self.a
self.a, self.b = self.b, self.a + self.b
self.count += 1
return value
# Usage
fib_iter = FibonacciIterator(10)
fib_list = list(fib_iter)
print(f"Fibonacci: {fib_list}")
# Fibonacci: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]Generator 구현
def fibonacci_generator(max_count):
a, b = 0, 1
count = 0
while count < max_count:
yield a
a, b = b, a + b
count += 1
# Usage
fib_gen = fibonacci_generator(10)
fib_list = list(fib_gen)
print(f"Fibonacci: {fib_list}")
# Fibonacci: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]Generator Pipeline (체이닝)
def read_large_file(file_path):
"""Generator: 파일에서 한 줄씩 읽기"""
with open(file_path, 'r') as f:
for line in f:
yield line.strip()
def filter_comments(lines):
"""Generator: 주석 줄 필터링"""
for line in lines:
if not line.startswith('#'):
yield line
def extract_words(lines):
"""Generator: 단어 추출"""
for line in lines:
for word in line.split():
yield word.lower()
# Pipeline - 각 단계가 lazy evaluation
def word_frequency(file_path):
lines = read_large_file(file_path)
non_comments = filter_comments(lines)
words = extract_words(non_comments)
freq = {}
for word in words:
freq[word] = freq.get(word, 0) + 1
return freq
# Memory efficient processing of large files
freq = word_frequency("large_log_file.txt")Performance Comparison
import time
import sys
def benchmark_list_approach(n):
"""List approach - all data in memory"""
start = time.time()
data = [x ** 2 for x in range(n)]
total = sum(data)
elapsed = time.time() - start
memory = sys.getsizeof(data)
return elapsed, memory
def benchmark_generator_approach(n):
"""Generator approach - lazy evaluation"""
start = time.time()
def squares():
for x in range(n):
yield x ** 2
total = sum(squares())
elapsed = time.time() - start
memory = sys.getsizeof(squares())
return elapsed, memory
n = 10_000_000
list_time, list_mem = benchmark_list_approach(n)
gen_time, gen_mem = benchmark_generator_approach(n)
print(f"List: {list_time:.3f}s, {list_mem:,} bytes")
print(f"Generator: {gen_time:.3f}s, {gen_mem} bytes")출력:
List: 1.234s, 89,095,160 bytes
Generator: 0.892s, 200 bytesLessons Learned
메모리 효율성: Generator는 데이터를 한 번에 메모리에 로드하지 않으므로 대용량 데이터 처리에 적합합니다.
Lazy Evaluation: Generator는 요청된 값만 생성하므로, 전체 시퀀스를 생성하지 않아도 됩니다.
한 번만 순회: Iterator와 Generator는 모두 한 번만 순회할 수 있습니다. 다시 순회하려면 새로 생성해야 합니다.
복잡한 로직에는 Iterator: 복잡한 상태 관리가 필요하면 Iterator 클래스가 더 명확할 수 있습니다.
간단한 변환에는 Generator: 간단한 데이터 변환 및 필터링에는 Generator가 더 간결하고 효율적입니다.
This blog does not accept any external sponsorships, affiliate marketing, or ad revenue.