[Python] 제너레이터(Generator)

hysong·2022년 10월 20일
0

Python

목록 보기
4/5

1 ~ 100,000,000 범위에서 임의의 숫자들을 골라 계산하는 프로그램을 만든다고 하자.
숫자를 고작 10개 선택해 그 합을 구할 경우, 메모리에 곧바로 숫자 1억 개를 보관해두는 것은 굉장히 비효율적일 것이다.
파이썬에서는 제너레이터(Generator)를 이용하여 단순히 생성 조건만 저장해둔 채 필요할 때마다 값을 반환할 수 있다.

📌 yield와 next(iterator[, default])

  1. yield 구문을 사용해 제너레이터를 리턴할 수 있는데, yield는 제너레이터가 현재 실행 중이던 값을 내보냄을 의미한다.
    (함수는 종료되지 않고 맨끝에 도달할 때까지 계속해서 실행된다.)
  2. next 함수를 호출해 다음 값을 생성할 수 있다.
def get_natural_number():
	n = 0
    while True:
    	n += 1
        yield n
        
        
g = get_natural_number()
for _ in range(100):
	print(next(g))
1
2
3
...
98
99
100

get_natural_number함수는 제너레이터를 리턴함을 확인할 수 있다.
>>> get_natural_number()
<generator object get_natural_number at 0x100eb37b0>

📌 Uses.

1.

range는 제너레이터의 방식을 활용하는 대표적인 함수이다.
range()는 range클래스를 리턴하는데, for문에서 사용할 경우에는 내부적으로 제너레이터의 next()를 호출하듯 매번 다음 숫자를 생성한다.

>>> range(5)
range(0, 5)
>>> list(range(5))
[0, 1, 2, 3, 4]
>>> type(range(5))
<class 'range'>
>>> for i in range(5):
...     print(i, end=' ')
...
0 1 2 3 4

2.

이 글의 서두에 언급한 계산기 프로그램을 작성한다고 하자.
1억 개의 숫자를 미리 다 생성해두는 것은 메모리에서 적지 않은 공간을 차지할 것이고, 생성하는 데 소요되는 시간 역시 상당히 클 것이다.
그러나 제너레이터를 리턴하듯 range 클래스를 리턴하면 그렇지 않다.
생성 조건만 정해두고 필요할 때 숫자를 생성해 꺼내 쓸 수 있다.

즉, 아래 코드의 a에는 이미 생성된 값들이 모두 담겨 메모리 공간을 차지하고, b에는 생성하기 위한 조건만 존재하는 것이다.

a = [n for n in range(100000000)]
b = range(100000000)
>>> len(a)
100000000
>>> len(b)
100000000

메모리 점유율을 확인해보면 range 클래스를 리턴하는 방식의 장점을 쉽게 알 수 있다. 변수 b는 항상 생성 조건만 보관하기 때문에 그 메모리 점유율은 생성하려는 숫자가 몇 개든 상관없이 동일할 것이다.
>>> sys.getsizeof(a)
835128600
>>> sys.getsizeof(b)
48

게다가 인덱스로 접근 시에는 값을 바로 생성하도록 구현되어 있어 리스트와 거의 동일한 느낌으로 편리하게 사용할 수 있다.
>>> b[999]
999

1개의 댓글

comment-user-thumbnail
2022년 10월 20일

참고 자료 :
파이썬 알고리즘 인터뷰 (박상길 지음)

답글 달기