몰입공간

[Python] 제너레이터 (Generator) 본문

Programming/Python

[Python] 제너레이터 (Generator)

sahayana 2022. 2. 24. 09:47


#1.  Generator

제너레이터(Generator)는 파이썬 iterator의 한 종류로, 말그대로 값을 '생성'하는 함수입니다. 

yield문을 통해 원하는 값이 있을때 마다 함수를 호출하고 값을 생성합니다.

즉, yield문이 있다면 그 함수는 제너레이터 함수입니다.

 

 

List, Tuple, Dict 등등 기존의 iterable한 파이썬 내장 컬렉션은 안에 있는 모든 데이터를 알아야 호출이 가능하고
그 데이터를 한번에 메모리에 적재합니다.

 

다만 그 데이터의 내용/처리시간/계산 등이 너무 방대하여 메모리 부하면에서 효율적이지 못한 경우, 혹은 무한루프로 처리해야 하는 반복문같은 경우 제너레이터를 통해 필요할때마다 호출하여 사용하는게 좋습니다.

 


#2.  Generator 생성

 

다음과 같이 몇 종류의 동물을 랜덤으로 뽑는 제너레이터 함수를 생성하였습니다.

import random

def pick_animal():  
    animals = ['사자', '호랑이', '코끼리', '뱀', '고양이', '고래', '낙타']
    while True:
        picked = random.choice(animals)
        yield picked

 

yield문이 있으므로 이 함수는 제너레이터 객체가 됩니다.

 

값을 요청할 때마다 yield문에 지정된 변수값을 도출합니다.

animal = pick_animal()  # 제너레이터 생성

print(animal.__next__())    # 값 출력
print(next(animal))
print(next(animal))
호랑이
고양이
사자

__next__() 혹은 next() 메서드를 통해 값을 하나씩 꺼내보거나, 다음과 같이 당연히 for문을 통해 호출이 가능합니다.

 for animal in pick_animal():
     print(animal)
    
     if len(animal) == 1:
         break
호랑이
코끼리
고래
사자
뱀	# break

제너레이터 함수는 yield 문을 만날 때마다 함수를 '일시정지'하고, yield에 지정된 값을 바깥으로 전달합니다.
그리고 다시 호출하여 반복적으로 값을 도출합니다.

 

따라서 return을 통해 함수 자체가 끝나는 일반적인 함수와 달리 제너레이터 함수는 종료전까지 함수안의 로컬 변수들을 그대로 저장할 수 있습니다.

 

def pick_animal():
    picked_animal = []
    animals = ['사자', '호랑이', '코끼리', '뱀', '고양이', '고래', '낙타']
    while True:
        picked = random.choice(animals)
        yield picked
        picked_animal.append(picked)
        print(len(picked_animal))

 

아까의 예제에서 picked_animal이라는 임의의 리스트를 생성하고 함수 실행마다 뽑은 동물을 저장하도록 구성하였습니다.

 

보통의 함수라면 기능이 종료 후 저장된 로컬 변수들이 사라지지만, 제너레이터 함수는 호출할 때마다 저장된 변수들이 사라지지 않는 것을 볼 수 있습니다.

animal = pick_animal()  # 제너레이터 생성

print(animal.__next__())    # 값 출력
print(next(animal))
print(next(animal))
고양이
1	# print(len(picked_animal)
코끼리
2
호랑이
3
고양이

#3.  yield from

 

다음과 같이 제너레이터 객체를 yield from 구문에 지정하여 값을 가져올 수 있습니다.

def animal_generator():
    yield from pick_animal()

animal = animal_generator()

print(animal.__next__())    # 값 출력
print(next(animal))
print(next(animal))
print(next(animal))
코끼리
1
고양이
2
고래
3
코끼리

 

yield from은 좀 더 간략한 코드를 위한 일종의 sugar syntax 입니다.

 


#4. Generator comprehension

 

우리가 익히 아는 List comprehension의 경우 [ ]로 묶어서 표현합니다.

동일한 표현식을 ( )로 묶는다면 이는 제너레이터 표현식이 됩니다.

 

list_comp = [i for i in range(1, 11)]
gen_comp = (i for i in range(1, 11))

print(list_comp)
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

print(gen_comp)
# <generator object <genexpr> at 0x0000021BF9F1FC80>

a = next(gen_comp)
print(a)
# 1

#5. 정리

 

  • yield 구문이 있다면 제너레이터 함수입니다.
  • 제너레이터 함수가 실행되면 yield 구문에서 일시정지하고 yield로 지정된 변수 값을 내보냅니다.
  • 따라서 일시정지할 때 제너레이터 객체 안의 로컬 변수도 그대로 저장됩니다.
  • yield from에 제너레이터 객체를 지정하는 형태로 간략하게 표현이 가능합니다.
  • 리스트 표현식을 ( )로 묶으면 제너레이터 객체가 됩니다.

#6. 참고

 


 

 

Comments