제네레이터는 제네레이터 객체에서 next 메서드를 호출할 때마다 함수 안의 yield 까지 코드를 실행하며 yield에서 값을 발생시킨다(generate). 그래서 이름이 제네레이터이다.
for 반복문은 반복할 때마다 next 를 호출하므로 yield에서 발생시킨 값을 가져온다. 그리고 StopIteration 예외가 발생하면 반복을 끝낸다.
참고로 제네레이터 객체에서 __iter__를 호출하면 self를 반환하므로 같은 객체가 나온다.
(제네레이터 함수 호출 > 제네레이터 객체 > __iter__는 self 반환 > 제네레이터 객체)
그런데 generate라는 키워드를 사용하면 되지 왜 yield라고 이름을 지었을까? yield는 생산하다라는 뜻과 양보하다라는 뜻도 가지고 있다.
즉, yield를 사용하면 값을 함수 바깥으로 전달하면서 코드 실행을 함수 바깥에 양보한다. 따라서 yield는 현재 함수를 잠시 중단하고 함수
바깥의 코드가 실행되도록 만든다.
yield의 동작 과정을 알아보기 위해 for 반복문 대신 next 함수로 next 메서드를 직접 호출해보자.
def number_generator():
yield 0 # 0을 함수 바깥으로 전달하면서 코드 실행을 함수 바깥에 양보
yield 1 # 1을 함수 바깥으로 전달하면서 코드 실행을 함수 바깥에 양보
yield 2 # 2을 함수 바깥으로 전달하면서 코드 실행을 함수 바깥에 양보
if __name__ == '__main__':
g = number_generator()
a = next(g) # yield를 사용하여 함수 바깥으로 전달한 값은 next의 반환값으로 나옴
print(a) # 0
b = next(g)
print(b) # 1
c = next(g)
print(c) # 2
yield를 사용하여 바깥으로 전달한 값은 next함수의 반환값으로 나온다고 했다. 따라서 next(g)의 반환 값을 출력해보면 yield에 지정한 값
0, 1, 2가 차례대로 나온다. 즉, 제네레이터 함수가 실행되는 중간에 next로 값을 가져온다.
제네레이터는 함수를 끝내지 않은 상태에서 yield를 사용하여 값을 바깥으로 전달할 수 있다. 즉, return은 반환 즉시 함수가 끝나지만
yield는 잠시 함수 바깥의 코드가 실행되도록 양보하여 값을 가져가게 한 뒤 다시 제네레이터 안의 코드를 계속 실행하는 방식이다.