1 ~ 100,000,000 범위에서 임의의 숫자들을 골라 계산하는 프로그램을 만든다고 하자.
숫자를 고작 10개 선택해 그 합을 구할 경우, 메모리에 곧바로 숫자 1억 개를 보관해두는 것은 굉장히 비효율적일 것이다.
파이썬에서는 제너레이터(Generator)를 이용하여 단순히 생성 조건만 저장해둔 채 필요할 때마다 값을 반환할 수 있다.
- yield 구문을 사용해 제너레이터를 리턴할 수 있는데, yield는
제너레이터가 현재 실행 중이던 값을 내보냄
을 의미한다.
(함수는 종료되지 않고 맨끝에 도달할 때까지 계속해서 실행된다.)- 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()
<generator object get_natural_number at 0x100eb37b0>
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
이 글의 서두에 언급한 계산기 프로그램을 작성한다고 하자.
1억 개의 숫자를 미리 다 생성해두는 것은 메모리에서 적지 않은 공간을 차지할 것이고, 생성하는 데 소요되는 시간 역시 상당히 클 것이다.
그러나 제너레이터를 리턴하듯 range 클래스를 리턴하면 그렇지 않다.
생성 조건만 정해두고 필요할 때 숫자를 생성해 꺼내 쓸 수 있다.
즉, 아래 코드의 a에는 이미 생성된 값들이 모두 담겨 메모리 공간을 차지하고, b에는 생성하기 위한 조건만 존재하는 것이다.
a = [n for n in range(100000000)]
b = range(100000000)
>>> len(a)
100000000
>>> len(b)
100000000
>>> sys.getsizeof(a)
835128600
>>> sys.getsizeof(b)
48
>>> b[999]
999
참고 자료 :
파이썬 알고리즘 인터뷰 (박상길 지음)