본문 바로가기
프로그래밍/Python

파이썬(Python) - 제너레이터(generator)에 대해서 알아보자.

by 부자 꽁냥이 2022. 9. 27.

파이썬(Python)에서는 제너레이터(generator)라는 것이 있는데 이는 메모리 절약을 위한 용도로 사용된다. 이번 포스팅에서는 제너레이터(generator)에 대하여 알아보려고 한다.


   제너레이터(generator)

1) 제너레이터(generator) 넌 누구냐?!

제너레이터(generator)는 쉽게 말해 return이 아닌 yield 문을 이용하여 순회 가능한(Iterable) 객체를 만들어내는 함수이다.


2) 제너레이터(generator) 생성하기

제너레이터는(generator)는 순회 가능한 객체를 yield 문으로 넘겨줘야 한다. 다음은 제너레이터를 생성하는 함수이다.

 

def generator(iterable):
    for x in iterable:
        yield x

 

또한 제너레이터 컴프리헨션을 통해서도 생성할 수 있다. 생성된 제너레이터는 바로 사용할 수는 없고 list 같은 것으로 감싸서 사용해야 한다. 아래 코드는 위에서 정의한 제너레이터 함수를 이용한 결과와 제너레이터 컴프리헤션을 통한 결과를 비교한 것이다. 사실 둘 다 똑같다.

 

gen1 = generator(range(5))
gen2 = (i for i in range(5)) ## 제너레이터 컴프리헨션

 

generator


3) 제너레이터(generator) 언제 쓰나요?

먼저 같은 역할을 하는 리스트보다 제너레이터가 차지하는 메모리 용량이 훨씬적다. 

 

import sys
test_num = 3
gen1 = (i for i in range(test_num))
list1 = list(range(test_num))

print('gen1의 용량 :', sys.getsizeof(gen1))
print('list1의 용량 :', sys.getsizeof(list1))

 

???

분명 제너레이터가 차지하는 메모리 용량이 적다고 했는데 이게 뭔가? 나랑 장난하자는건가? 당연히 아니다. 제너레이터의 효과는 큰 사이즈를 갖는 데이터에서 나타난다. test_num을 1,000,000으로 해보겠다.

 

import sys
test_num = 1000000
gen1 = (i for i in range(test_num))
list1 = list(range(test_num))

print('gen1의 용량 :', sys.getsizeof(gen1))
print('list1의 용량 :', sys.getsizeof(list1))

 

!!!

제너레이터의 메모리 용량은 120으로 고정된 반면 리스트의 메모리 용량은 엄청나게 늘어났다. 

 

제너레이터 자체로 메모리 용량이 적지만 한번 사용하고 나면 바로 비어있으므로 프로그램 실행 중에도 메모리를 아낄 수 있다.

 

test_num = 10
gen1 = (i for i in range(test_num))
print(list(gen1)) ## 최초 한번 실행시 출력이 되고
print(list(gen1)) ## 두 번째로 사용하려고 하면 빈털털이가 되어있다.

 

제너레이터는 두 번째 사용시에 아무것도 없다.

그러나...

제너레이터의 단점 또한 존재한다. 그것은 바로 계산 속도가 일반적으로 느리다. 아래 코드는 합계를 계산할 때 제너레이터와 리스트로 주어진 경우의 시간이 얼마나 차이 나는지 확인한다.

 

import time

def sum_with_generator(iterable):
    start = time.perf_counter()
    sum((i for i in iterable))
    return time.perf_counter()-start

def sum_with_list(iterable):
    start = time.perf_counter()
    sum(iterable)
    return time.perf_counter()-start
    
print('제너레이터 합계 계산 시간 :', sum_with_generator(range(1000000)), 'sec')
print('리스트 합계 계산 시간 :', sum_with_list(list(range(1000000))), 'sec')

 

리스트로 처리하는 것이 더 약 3배 정도 더 빠르다. 따라서 메모리가 큰 이슈가 아니라면 제너레이터보다는 리스트로 처리하는 것이 더 낫다.

 

또한 배열을 여러 군데에서 사용한다면 이 역시 제너레이터보다 리스트로 처리하는 것이 더 낫다. 왜냐하면 앞에서도 언급했듯이 제너레이터는 한번 사용하고 나면 없어지기 때문이다.


댓글


맨 위로