# 이번에는 제너레이터(발생자)에 대해 알아보자.
# 제너레이터는 함수 안에서 yield 라는 키워드만 사용하면 끝이다.
# yield 값
# 위와 같은 형식으로 사용한다.
# yield를 이용해서 제너레이터를 만들고 사용해보자.
defnumber_generator():yield0yield1yield2foriinnumber_generator():print(i,end=' ')# 0 1 2 가 출력된다. 이터레이터와 사용 방법이 똑같다.
print()# number_generator 함수로 만든 객체가 이터레이터인지 dir 함수를 이용해 확인해보자.
g=number_generator()print(g)# <generator object number_generator at 0x0000020265C4E7B0>
print(dir(g))# [..., '__iter__', ..., '__next__', ...]
# 실제로 이터레이터에서 볼 수 있는 __iter__, __next__ 메소드가 들어있다.
# 이 객체에 대해 __next__를 호출해보면 0, 1, 2가 나오다가 StopIteration 예외가 발생한다.
try:whileTrue:print(g.__next__(),end=' ')exceptStopIteration:print('StopIteration')# 0 1 2 StopIteration 가 출력된다.
# 이처럼, 함수에 yield만 사용해서 간단하게 이터레이터를 구현할 수 있다.
# 제너레이터는 제너레이터 객체에서 __next__ 메소드를 호출할 때마다
# 함수 안의 yield까지 코드를 실행하며 yield에서 값을 발생시킨다.
# yield는 생산하다라는 뜻 뿐만 아니라 양보하다 라는 뜻도 가지고 있다.
# 즉, yield를 사용하면 값을 함수 바깥으로 전달하며 코드 실행을 함수 바깥에 양보한다.
# 따라서 yield는 현재 함수를 잠시 중단하고 함수 바깥의 코드가 실행되도록 만든다.
# 제너레이터는 함수 끝까지 도달하면 StopIteration 예외가 발생한다.
# 마찬가지로 return도 함수를 끝내므로 return을 사용해서 함수 중간에 빠져나오면 StopIteration 예외 발생
# 제너레이터 안에서 return에 반환값을 지정하면 발생하는 StopIteration 예외의 에러 메시지로 들어간다.
제너레이터 만들기
# 이번에는 range(n)처럼 동작하는 제너레이터를 만들어보자.
defnumber_generator(stop):n=0# 0부터 시작
whilen<stop:# 현재 숫자가 stop보다 작을 때동안 반복
yieldn# 현재 숫자를 바깥으로 전달
n+=1# 현재 숫자 증가
foriinnumber_generator(3):print(i,end=' ')# 0 1 2
print()# yield에서 다른 의미있는 값을 반환하는 것으로 응용해보자.
defupper_generator(x):foriinx:yieldi.upper()alphabets=['a','b','c','d','e']foriinupper_generator(alphabets):print(i,end=' ')# A B C D E
print()# 이처럼 yield를 잘 이용하면 __iter__과 __next__를 구현해서 이터레이터를 만드는 것보다 훨씬 간단하게 구현을 할 수 있다.
yield from으로 값을 여러번 바깥으로 전달하기
# 여태는 yield로 값을 한 번씩 바깥으로 전달했기 때문에 값을 여러 번 전달할 때에는 반복문을 이용했다.
# 이런 경우에 반복문을 쓰지 않고 yield from을 사용하면 된다.
# yield from 반복가능한 객체
# yield from 이터레이터
# yield from 제너레이터 객체
# yield from을 이용해서 리스트의 숫자들을 바깥으로 전달해보자.
defnumber_generator():x=[1,2,3]yieldfromxforiinnumber_generator():print(i,end=' ')# 1 2 3 이 출력된다.
print()# 리스트 표현식을 사용할 때 [] 를 이용했는데,
# 같은 리스트 표현식을 ()로 묶으면 제너레이터 표현식이 된다.
# 리스트 표현식은 처음부터 리스트의 요소를 만들어내지만 제너레이터 표현식은 필요할 때 만드므로 메모리를 절약할 수 있다.
print([iforiinrange(10)ifi%2==0])# [0, 2, 4, 6, 8]
print((iforiinrange(10)ifi%2==0))# <generator object <genexpr> at 0x00000200FE639510>
Leave a comment