40. 제너레이터

제너레이터란?

제너레이터는 이터레이터를 생성해주는 함수이다.
이전의 이터레이터 파트에서, 이터레이터를 만들기 위해서는 클래스 안에 iter, next, getitem을 구현해야 하지만, 제너레이터는 yield만 사용하여 간단하게 작성할 수 있다.

** 제너레이터 알아보기
함수 안에서
yield** 를 사용하면 그 함수 제너레이터가 된다.

def number_generator():
    yield 0
    yield 1
    yield 2
 
for i in number_generator():
    print(i)

위코드에서 우리는 yield를 사용해 제너레이터를 만들고, for문을 이용해 yield로 지정한 값 0,1,2를 출력하였다.

제너레이터는 이터레이터와 기본적인 동작이 동일하다.
이터레이터는 next 메소드에서 직접 값을 반환하지만, 제너레이터는 yield로 지정한 값이 next 메소드의 반환값으로 난오게 된다.
또한 제너레이터는 함수의 끝까지 도달하면 자동으로 stopiteration이 발생한다.

for와 제너레이터


위 그림을 통해 for로 제너레이터 객체를 반복하는 구조를 확인 할 수 있다.

yield의 동작 과정 알아보기

def number_generator():
    yield 0    # 0을 함수 바깥으로 전달하면서 코드 실행을 함수 바깥에 양보
    yield 1    # 1을 함수 바깥으로 전달하면서 코드 실행을 함수 바깥에 양보
    yield 2    # 2를 함수 바깥으로 전달하면서 코드 실행을 함수 바깥에 양보
 
g = number_generator()
 
a = next(g)    # yield를 사용하여 함수 바깥으로 전달한 값은 next의 반환값으로 나옴
print(a)       # 0
 
b = next(g)
print(b)       # 1
 
c = next(g)
print(c)       # 2

yiled를 사용해 전달한 값은 next 메소드의 반환값으로 나오게 된다.

위 그림으로 자세한 동작구조를 확인해 보자
먼저 g = number_generator()로 제너레이터 객체를 생성한다.
그 후, a에 next(g)를 할당하는데, 이때 제너레이터 안의 yield 0이 실행돼 함수 밖으로 숫자 0을 전달하고, 실행을 양보한다.
따라서 함수 밖에서 print(a)를 통해 a값이 출력된다.
이 순서대로 a, b, c값이 출력되게 된다.

이처럼 제너레이터는 함수를 끝내지 않은 상태에서 yield를 사용해 값을 바깥으로 전달하고 실행을 양보한다.

제너레이터 만들기

range처럼 동작하는 제너레이터를 만들어보자

def number_generator(stop):
    n = 0              # 숫자는 0부터 시작
    while n < stop:    # 현재 숫자가 반복을 끝낼 숫자보다 작을 때 반복
        yield n        # 현재 숫자를 바깥으로 전달
        n += 1         # 현재 숫자를 증가시킴
 
for i in number_generator(3):
    print(i)
    
   # 0 1 2
  

제너레이터 안에서 n을 만들고, n이 stop보다 작을때까지 1씩 증가시키게끔 구현하면 된다.

yield에서 함수 호출하기

yield에서 메소드를 호출하면 어떻게 될까?

다음과 같이 제너레이터를 만들때, yield에서 함수를 호출하면 실행 결과에 함수를 호출한 결과가 나오게 된다.

def upper_generator(x):
    for i in x:
        yield i.upper()    # 함수의 반환값을 바깥으로 전달
 
fruits = ['apple', 'pear', 'grape', 'pineapple', 'orange']
for i in upper_generator(fruits):
    print(i)

여기서 yield는 upper함수를 통해 대문자가 된 문자열을 밖으로 전달하는 역할을 한다. yield에 무엇을 지정하든, 결과만 바깥으로 전달한다.

40.3 yield from으로 값을 여러 번 바깥으로 전달하기

여태까지는 yield를 통해 값을 함수 밖으로 전달할때 하나씩 전달했고, 여러번 전달할때는 for 또는 while 을 사용했다.

for 또는 while 대신, yield from을 사용하면 더욱 간단하게 여러 값을 반환할 수 있다.

def number_generator():
    x = [1, 2, 3]
    yield from x    # 리스트에 들어있는 요소를 한 개씩 바깥으로 전달
 
for i in number_generator():
    print(i)

yield from에 반복 가능한 객체를 지정하면, 객체에 담긴 요소를 한개씩 바깥으로 전달한다.

yield from에 제너레이터 객체 지정하기

yield from에 제너레이터 객체를 지정하면 어떻게 될까?
다음 코드를 살펴보자.

def number_generator(stop):
    n = 0
    while n < stop:
        yield n
        n += 1
 
def three_generator():
    yield from number_generator(3)    # 숫자를 세 번 바깥으로 전달
 
for i in three_generator():
    print(i)

가장 위의 제너레이터 함수는 stop 인자 직전까지의 숫자를 만들어 낸다. 그리고 three_generator()에서 yield from에 제너레이터 객체를 지정하면 three_generator()는 숫자를 세번 함수 밖으로 전달하게 된다.

좋은 웹페이지 즐겨찾기