앞선 블로그에서 Iterator와 Generator에 대하여 각각 설명하였습니다. 이번 블로그에서는 둘 사이의 차이점을 알아보도록 하겠습니다. Iterator는 __iter__()와 __next__() 메서드를 통해 데이터 요소를 순차적으로 반환하는 객체입니다. 따라서 사용자 정의 Iterator를 만들기 위해서는 두개의 메서드를 구현해 주어야 합니다.
하지만, Generator는 저 두 함수를 구현하지 않아도 손쉽게 Iterator를 만들 수 있게 해줍니다. 즉, yield 키워드를 사용하여 다음 데이터를 반환할 수 있도록 함수를 구현해주면 그 함수는 Iterator와 동일하게 동작하게 됩니다. 그래서 Iterator를 생성해내는 함수라고도 말합니다.
피보나치 수열을 사용한 예제로 비교해 보겠습니다.
Iterator는 __iter__ 함수와 __next__ 함수를 구현한 클래스로 정의합니다.
class fib:
def __init__(self):
self.prev = 0
self.curr = 1
def __iter__(self):
return self
def __next__(self):
value = self.curr
self.curr += self.prev
self.prev = value
return value
f = fib()
list(islice(f, 0, 10)) # islice는 iterable한 객체 f의 0-9까지의 요소를 iterator로 반환하는 함수
[1, 2, 3, 5, 8, 13, 21, 34, 55]
Generator 는 yield를 사용한 함수로 구현합니다.
def fib():
prev, curr = 0, 1
while True:
yield curr
prev, curr = curr, prev + curr
f = fib()
list(islice(f, 0, 10))
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Generator는 보통 일회성 객체로서 메모리를 절약하며, 필요할 때마다 데이터를 생성합니다. 이에 비해 Iterator는 메모리에 데이터를 미리 준비해 두는 경우가 많아 메모리 사용량이 상대적으로 큽니다. 리스트를 Iterator로 변환하여 사용하는 것을 생각해 보면 이해가 빠르실 것입니다. 이미 메모리에 있는 데이터를 순차적으로 반복해 줄 뿐이죠.
결론적으로 Generator는 Iterator 대비 다음과 같은 장점이 있습니다.
1. 메모리 효율성: Generator는 값을 필요할 때 생성하므로 메모리를 절약합니다. 예를 들어, 큰 데이터 세트를 한 번에 처리할 필요 없이 하나씩 계산하여 반환할 수 있습니다.
2. 코드 간결성: yield를 사용해 Generator를 정의하면 Iterator보다 간결하고 직관적인 코드 작성이 가능합니다.
3. 지연 계산 (Lazy Evaluation): Generator는 지연 계산을 지원하여 값이 실제로 필요할 때까지 계산을 미루므로, 성능 향상에 기여할 수 있습니다.