파이썬 웹 프레임워크 Fastapi를 공부하던 도중에 generator를 만드는 과정이라는 말을 듣고 궁금해져 공부해보려고 한다.
함수가
iterator와 같이 동작하도록 선언하는 함수들
iterator와 같이 동작한다는 말은 또 무슨말인가?
iterator 편을 참고하자.
다시 generator로 돌아와, 예시를 통해 이해해보려고 한다.
def first_n(n):
'''Build and return a list'''
num, nums = 0, []
while num < n:
nums.append(num)
num += 1
return nums
sum_of_first_n = sum(first_n(1000000))
위의 코드에서는 1000000개의 숫자로 이루어진 list가 만들어지고 메모리는 이렇게 큰 양의 숫자를 견딜 수 없을 것이다.
위의 예시를 generator 패턴으로 작성해보자.
# Using the generator pattern (an iterable)
class first_n(object):
def __init__(self, n):
self.n = n
self.num = 0
def __iter__(self):
return self
# Python 3 compatibility
def __next__(self):
return self.next()
def next(self):
if self.num < self.n:
cur, self.num = self.num, self.num+1
return cur
raise StopIteration()
sum_of_first_n = sum(first_n(1000000))
동작은 같지만, 생성자가 많아 복잡하게 얽혀있게 보인다.
따라서 파이썬에서는 간단하게 generator function을 작성할 수 있도록 shortcut을 제공한다.
# a generator that yields items instead of returning a list
def firstn(n):
num = 0
while num < n:
yield num
num += 1
sum_of_first_n = sum(firstn(1000000))
yield 를 사용한 것을 주목해야 한다.
동일한 결과 값을 제공하는 함수 두개가 있다.
function1
import time
def abc():
alphabets = []
for c in "ABC":
time.sleep(1)
alphabets.append(c)
return alphabets
for c in abc():
print(c)
function2
import time
def abc():
for c in "ABC":
time.sleep(1)
yield c
for c in abc():
print(c)
결과값을 하나씩 메모리에 올려놓기 때문에 성능상의 이점이 있다.
iterator를 사용하려는데 데이터의 양이 너무 많거나 메모리가 부족한 경우에 generator를 사용하여 성능상의 이슈 없이 처리할 수 있다.
fastapi에서 세션처리하는 코드에 yield 키워드를 썼는데 그 이유도 세션은 계속 유지되어야 하는 부분이라 성능상의 문제를 일으키지 않기위한 것이었다.