Python Generator

김현우·2020년 7월 27일
0
post-thumbnail

파이썬에서 보통의 함수는 값을 반환하고 종료 하지만 제너레이터 함수는 값을 반환하기는 하지만 산출(yield)한다는 차이점이 있습니다. 그리고 제너레이터는 쉽게 얘기하면 이터레이터를 생성해주는 함수라고도 볼 수 있습니다.

다음 코드를 보면 함수안에서 yield를 사용하여 리스트의 제곱을 산출하는 함수가 있고, 이 함수를 print문으로 확인해보면 generator object 임을 확인할 수 있습니다.

def generator_squares():
    for i in range(3):
        yield i ** 2


print("gen object=", end=""), print(generator_squares())

gen object=<generator object generator_squares at 0x10f3b0150>

yield는 제너레이터 함수에서 값을 반환할 때 사용되며 yield 호출후에 다시 next가 호출될때 까지 현재의 상태에서 머물고 있다가 next 함수가 호출되면 이전의 상태에 이어서 다음 연산을 수행.

print(dir(generator_squares()))를 수행하여 보면,

def generator_squares():
    for i in range(3):
        yield i ** 2
        
print("dir gen =", end=""), print(dir(generator_squares()))

dir gen =['__class__', '__del__', '__delattr__', '__dir__', '__doc__',
'__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
'__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__name__',
'__ne__', '__new__', '__next__', '__qualname__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__',
'close', 'gi_code', 'gi_frame', 'gi_running', 'gi_yieldfrom', 'send', 'throw']

__iter____next__ 함수가 둘다 들어있어, __iter__를 호출한 후에 __next__함수를 호출하지 않아도 __next__를 바로 호출할 수 도 있습니다.

📌 이터레이터와 마찬가지로 next 함수로 값을 꺼내올 수 있고 for문이 끝나게 되면 StopIteration 이 발생!!

제너레이터 함수는 실행중에 send 함수를 통해서 값을 전달가능!!
예제를 보면,

def generator_send():
    received_value = 0

    while True:

        received_value = yield
        print("received_value = ",end=""), print(received_value)
        yield received_value * 2

gen = generator_send()
next(gen)
print(gen.send(2))

next(gen)
print(gen.send(3))

출력결과
received_value = 2
4
received_value = 3
6

generator_send 함수는 yield로 send를 통해서 받은 값을 received_value에 할당하고 그 값의 2배 수를 리턴받고 있습니다. 제너레이터에서는 이처럼 yield를 이용해서 제너레이터 함수 실행중 값을 전달 할 수 있고, 응용하면 제너레이터 함수를 사용해서 main 실행 루프에서 연산결과에 따라 호출도 제어할 수 있습니다.

📌 제너레이터 표현식은 list comprehension과 비슷한데 괄호만 []가아니고 ()로 변경면 해주면 Generator표현식이 됩니다.

Assignment

이번 과제는 다음코드를 실행해보고 분석한 결과를 블로깅하는 과제 입니다. lazy evaluation 이란 무엇인지와 장점 및 리스트 컴프리헨션과의 차이점에 대하여 블로깅 해주세요.

import time

L = [ 1,2,3]

def print_iter(iter):
    for element in iter:
        print(element)

def lazy_return(num):
    print("sleep 1s")
    time.sleep(1)
    return num

print("comprehension_list=")
comprehension_list = [ lazy_return(i) for i in L ]
print_iter(comprehension_list)

print("generator_exp=")
generator_exp = ( lazy_return(i) for i in L )
print_iter(generator_exp)

''' 
# 결과

comprehension_list=
sleep 1s
sleep 1s
sleep 1s
1
2
3

generator_exp=
sleep 1s
1
sleep 1s
2
sleep 1s
3
'''

generator expression는 일반적인 반복문과는 연산하는 절차가 다르다. 제너레이터는 값을 반환해야 할 때 제너레이터 안의 연산을 잠시 멈추고 외부로 값을 전달함.

따라서 lazy_return(num) 함수에서 return한 값을 가지고 있으면서 print_iter(iter) 함수를 처리해주는 과정을 반복함.

반면 list comprehension은 lazy_return(num) 함수를 모두 처리한 후 print_iter(iter) 함수를 실행.

generator expression는 lazy evaluation, 즉 지연 평가 방식으로 작동. 리소스가 낭비되는 것을 방지하기 위해 lazy evaluation을 사용하게 되면 필요한 값을 그때그때 처리하기 때문에 메모리를 더 효율적으로 사용할 수 있음

list comprehension 연산 방식으로 반복문을 한 번에 처리하면 메모리가 그만큼 값을 저장하고 있으므로 서비스 성능을 저하시킬 수 있다. 따라서 함수를 호출 했을 때 연산을 해두고 결과값을 이용하지 않는 경우 메모리의 낭비가 일어남.

profile
코딩을 잘하는 개발자가 되자!

0개의 댓글