[Python] generator(제너레이터) 정리
Generator
Generator(제너레이터)
:iterator
를 생성해 주는 함수iterator
:next()
메서드를 이용해 데이터에 순차적으로 접근이 가능한 객체
generator
는 일반적인 함수와 비슷해보이지만, 가장 큰 차이점은yield
라는 구문일 것이다. 아래의 예시를 보자def generator(n): i = 0 while i < n: yield i i += 1
일반 함수와의 차이는
yield
밖에 없다.yield
는 무엇인가?yield
는generator
가 일반 함수와 구분되는 가장 핵심적인 부분이다.일반적인 함수의 경우, 사용이 종료되면 결과 값을 호출부로 반환한 뒤 함수 자체를 종료시킨 후 메모리 상에서 정리(clear)된다.
하지만,
yield
는 다르다.generator
함수가 실행 중에yield
를 만날 경우, 해당 함수는 그 상태로 정지되며, 반환 값을next()
를 호출한 쪽으로 전달한다. 이후 해당 함수는 일반적인 경우처럼 종료되는 것이 아니라 그 상태로 유지되게 된다. 즉, 함수에서 사용된 로컬 변수나instruction pointer
등과 같은 함수 내부에서 사용된 데이터들이 메모리에 그대로 유지된다.def generator(n): i = 0 while i < n: yield i i += 1 for x in generator(5): print(x) # 출력 0 1 2 3 4
위 구문을 하나씩 살펴보자.
- for 문이 실행되며
generator
함수가 호출된다. generator
함수는 일반 함수와 동일한 절차로 실행된다.- 실행 중 while 문 안에서
yield
를 만나게 된다. 그러면 return 과 비슷하게 함수를 호출했던 구문으로 반환하게 된다. 여기서는 첫번째 i 값인 0을 반환한다. 하지만 반환했다고generator
함수가 종료되는 것이 아니라 그대로 유지한 상태이다. - x 값에는
yield
에서 전달된 0 값이 저장된 후 출력된다. 그 후 for 문에 의해 다시generator
함수가 호출된다. - 이 때,
generator
함수는 처음부터 시작되는 것이 아니라yield
이후부터 시작된다. 따라서"i += 1"
문장이 실행되고 i 값은 1로 증가한다. - 아직 while 문 내부이기 때문에
yield
구문을 만나 i 값인 1이 전달된다. - x 값은 1을 전달 받고 출력된다. (이후 반복)
- for 문이 실행되며
Generator Expression
Generator Expression
:generator
함수를 더 쉽게 사용하기 위해 제공되는 것list comprehension
과 비슷하지만[]
대신()
를 사용예제
# 0 ~ 9 사이 정수 중 홀수를 list와 generator object로 생성 >>> [i for i in xrange(10) if i % 2] [1, 3, 5, 7, 9] >>> (i for i in xrange(10) if i % 2) <generator object <genexpr> at 0x7f6105d90960>
()
를 사용하면 위와 같이generator object
로 생성된다.
Generator 사용 이유
메모리를 효율적으로 사용할 수 있다.
예제
>>> import sys # list >>> sys.getsizeof( [i for i in xrange(100) if i % 2] ) 536 >>> sys.getsizeof( [i for i in xrange(1000) if i % 2] ) 4280 # generator >>> sys.getsizeof( (i for i in xrange(100) if i % 2) ) 80 >>> sys.getsizeof( (i for i in xrange(1000) if i % 2) ) 80
list
의 경우 사이즈가 커질수록 메모리 사용량이 늘어남. 반면generator
의 경우 사이즈가 커져도 차지하는 메모리 사이즈는 동일함- 추가 설명
list
는list
안에 속한 모든 데이터를 메모리에 적재하기 때문에list
의 크기만큼 차지하는 메모리 사이즈가 늘어나게 된다. 반면generator
는 데이터 값을 한꺼번에 메모리에 적재하는 것이 아니라next()
메서드를 통해 차례로 값에 접근할 때마다 메모리에 적재한다. 따라서list
의 규모가 큰 값을 다룰수록generator
의 효율성은 높아진다.
- 추가 설명
Lazy Evaluation
효과를 볼 수 있다.Lazy Evaluation
: 계산 결과 값이 필요할 때까지 계산을 늦추는 효과예제
def sleep_fucn(x): print("sleep") time.sleep(1) return x
sleep_func()
는 1초간sleep
을 수행한 뒤 x 값을 반환하는 함수이다. 만약 위sleep_func()
함수를 이용해list
와generator
를 생성하면 어떻게 동작할까?list
예제
list = [sleep_func(x) for x in xrange(5)] for i in list: print(i) # 출력 sleep... sleep... sleep... sleep... sleep... 0 1 2 3 4
generator
예제
gen = (sleep_func(x) for x in xrange(5)) for i in gen: print i # 출력 sleep... 0 sleep... 1 sleep... 2 sleep... 3 sleep... 4
list
의 경우,list
의 모든 값을 먼저 수행하기 때문에sleep_func()
함수를xrange()
값만큼 한번에 수행한다. 만일sleep_func()
에서 수행하는 시간이 길거나list
값이 매우 큰 경우 처음 수행할 때 그만큼 부담으로 작용된다.
반면,
generator
의 경우generator
생성 시 실제 값을 로딩하지 않고, for 문이 수행될 때 하나씩sleep_func()
를 수행하며 값을 불러오게 된다. 수행 시간이 긴 연산을 필요한 순간까지 늦출 수 있다는 점이 특징이다.
이러한 특징을 이용하면, fibonacci 수열과 같은 작업을 간결한 문법과 더불어 매우 효율적으로 코드를 작성할 수 있다.
def fibonacci_func(n): a,b = 0, 1 i = 0 while True: if (i > n): return yield a a, b = b, a+b i += 1 fib = fibonacci_func(10) for x in fib: print(x)
'프로그래밍 언어 > Python' 카테고리의 다른 글
[Python] split 함수 (0) | 2021.03.02 |
---|---|
[Python] GIL(Global Interpreter Lock) 정리 (0) | 2021.02.28 |
[Python] bisect (이진 탐색) (0) | 2021.02.04 |
[Python] heapq (최소힙, 최대힙) (0) | 2021.02.03 |
[Python] permutations & combinations (순열과 조합) (0) | 2021.02.02 |