제너레이터 (발생자)
- 이터레이터를 생성해주는 함수이다.
- 이터레이터는 클래스
__iter__
, __next__
, __getitem__
를 구현해야 하지만, 제너레이터 함수는 안에서 yield 라는 키워드만 사용하면 끝이다.
iterator 와 차이점
- iterator 는 __next__ 메서드 안에서 직접 return 으로 값을 반환하지만, generator 는 yield 에 지정한 값이 __next__ 메서드의 반환값으로 나온다.
- iterator 는 raise 로 StopIteration 예외를 직접 발생시켰지만, generator 는 함수의 끝까지 도달하면 StopIteration 예외가 자동으로 발생한다.
- generator 는 generator 객체에서 __next__ 메서드를 호출할 때마다 함수 안의 yield 까지 코드를 실행하며 yield 에서 값을 발생시킨다.
yield
함수 안에서 yield 를 사용하면 함수는 제너레이터가 되고, yield 에는 값(변수)를 지정한다.
- yield 값
- yield 를 사용하면 값을 함수 바깥으로 전달하면서 코드 실행을 함수 바깥에 양보한다. 따라서 yield 는 현재 함수를 잠시 중단하고, 함수 바깥의 코드가 실행되도록 만든다.
generator & return
제너레이터는 함수를 끝내지 않은 상태에서 yield 를 사용하여 값을 바깥으로 전달한다. return 은 반환 즉시 함수가 끝나지만, yield 는 잠시 함수 바깥의 코드가 실행되도록 양보하여 값을 가져가게 한 뒤 다시 제너레이터 안의 코드를 계속 실행하는 방식이다.
제너레이터는 함수 끝까지 도달하면 StopIteration 예외가 발생하는데,
return 을 사용해서 함수 중간에 빠져나오면 StopIteration 예외가 발생한다.
yield from
- yield from 반복가능한객체
- yield from 이터레이터
- yield from 제너레이터객체
제너레이터 표현식(generator expression)
제너레이터 표현식은 Lazy evaluation 을 위해서 사용될 수 있다.
- Lazy evaluation - 실행을 지연시킨다는 의미
- () 를 사용해서 생성한다.
코드 분석
리스트 생성, 출력 (no generator)
import time
L = [1, 2, 3]
def print_iter(iter):
for element in iter:
print(element)
def lazy_return(num):
print("sleep 1s")
time.sleep(1)
return num
print("comprehension_list=")
comprehension_list = [ lazy_return(i) for i in L ]
print_iter(comprehension_list)
sleep 1s
sleep 1s
sleep 1s
1
2
3
- 리스트 표현식이 사용되어 1, 2, 3 이라는 요소가 각 1초씩 딜레이가 되어 comprehension_list 라는 변수에 값이 적재된다.
- 새로운 리스트를 생성하는 처리가 다 끝난 후, 리스트를 한 번에 출력한다.
리스트 생성, 출력 (with generator)
import time
L = [1, 2, 3]
def print_iter(iter):
for element in iter:
print(element)
def lazy_return(num):
print("sleep 1s")
time.sleep(1)
return num
print("generator_exp=")
generator_exp = ( lazy_return(i) for i in L )
print_iter(generator_exp)
sleep 1s
1
sleep 1s
2
sleep 1s
3
- 제너레이터 표현식으로 생성된 1 이라는 요소를 만들고 출력한다.
- 제너레이터 표현식으로 생성된 2 라는 요소를 만들고 출력한다.
- 제너레이터 표현식으로 생성된 3 라는 요소를 만들고 출력하고, 처리가 끝나면 종료된다.
1. lazy evaluation 이란 무엇인지와 장점
- 일반적으로 range() 를 사용하여 임의의 숫자 list를 생성한다고 했을 때, 한 번의 실행으로 range() 함수 안에 지정된 숫자 만큼의 요소를 한 번에 생성하여 메모리에 저장한다.
- 하지만 generator 는 전체 range()를 저장하는 것 대신에.. 요소를 만들어 내는 식을 저장해두고, 오직 필요할 때에만 다음의 값을 생성해는데, 이것을 lazy evaluation 이라고 한다.
list 표현식으로 만들어지는 것은 [0, 1, 2, 3...]
의 느낌.
제너레이터 표현식으로 만들어지는 것은 (i=0; i<10; i+=1)
의 느낌.
장점은, 당연히 메모리 효율이 좋을 것 같다.
한 번에 모든 데이터를 준비해두는 것 보다, 원할 때 원하는 값을 생성해서 다룰 수 있으니 메모리 효율에 좋을 것 같다.
2. 리스트 컴프리헨션과의 차이점에 대하여
- 리스트는 모든 요소를 그것이 생성될 때 저장한다. 하지만 제너레이터는 원할 때 그 다음의 요소를 생성한다.
- 리스트는 필요한 만큼 반복되어질 수 있지만, 제너레이터는 오직 한 번만 반복된다.
- 리스트는 인덱스로 요소를 추출할 수 있지만, 제너레이터는 인덱스를 사용할 수 없으며, 처음부터 끝까지 오직 값만 생성한다.
참고자료