Python - Generator

enjoywater·2020년 8월 26일
0

TIL

목록 보기
15/36
post-thumbnail

Generator

Generator란, wikipedia에 따르면

컴퓨터과학에서 loop의 반복 동작을 제어하는데 사용할 수 있는 루틴이다. 모든 generator는 iterator이기도 하다. -중략

으로 정의되어있다.

쉽게 이야기하면 iterator를 생성하주는 함수라고도 볼 수 있으며, generator함수 안에서 yield라는 키워드를 사용한다.

일반적으로 파이썬에서 함수는 값을 반환하고 종료하지만,
generator 함수는 값을 반환하기는 하지만 산출(yield)한다는 차이점이 있다.

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

다음의 코드를 보면,

def test_gen():
    yield 1
    yield 2

result = test_gen()

print(dir(test_gen()))
print(result.__next__())
print(result.__next__())
print(result.__next__())

# 결과값 ->
# ['__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']
# 1
# 2
# StopIteration

test_gen함수속에 __iter__함수와 __next__함수가 모두 들어있는 것을 볼 수 있다.
때문에 __next__ 함수를 바로 호출할 수 있다.

또한, __next__함수가 실행이 되면서 차례대로 값을 반환하다가 모두 반환하면 끝나버리는 것을 볼 수 있다.

generator는 함수외에도 표현식으로도 나타낼 수 있는데, Lazy evaluation을 위해 사용 될 수 있다.
Lazy evaluation은 말 그대로 실행을 지연시킨다는 의미이다.

표현식의 문법은 list comprehension과 비슷하지만 대괄호 - [ ]가 아닌 괄호 - ( )를 사용하여 만든다.
코드를 통해 확인해보도록 하자.

test_li = [ 1, 2, 3, 4 ]

def test_gen():
    result = ( x + 10 for x in test_li )
    print(f"{result} -> test_gen")
    return result

def print_iter(iter):
    for element in iter:
        print(f"{element} -> print_iter")

print_iter(test_gen())

# 결과값 -> 
<generator object test_gen.<locals>.<genexpr> at 0x7ff9713724a0> -> test_gen
11 -> print_iter
12 -> print_iter
13 -> print_iter
14 -> print_iter

test_gen 함수 속 result변수에 표현식으로 담긴 generator object를 확인 할 수 있다.



예제

  • 아래의 코드에서 lazy evaluation이란 무엇인지와, 장점 및 list comprehension과의 차이점에 대하여 설명
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) # 1초간 정지
    print(f"lazyreturn {num}")
    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)

위에서도 잠시 언급했던 Lazy Evaluation을 위 코드의 결과값으로 확인해 볼 수 있다.
어떤 값이 실제로 쓰일 때 까지 그 값의 계산을 뒤로 미루는 동작방식이다.
해당 코드의 결과값을 보면,

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

generator_exp=
sleep 1s
lazyreturn 1
1
sleep 1s
lazyreturn 2
2
sleep 1s
lazyreturn 3
3

으로 출력이 된다.
sleep 1s가 print출력된 후, sleep메소드가 동작하여 1초간 정지하게 된다.
이후 lazy_return에 들어온 parameter를 하나씩 출력하게된다.


크게 comprehension_list= 이하의 결과값과, generator_exp=이하의 결과값의 순서가 다른 것을 볼 수 있다.

  • comprehension_list=
    list_comprehension을 사용하게 되면 list모든 값을 먼저 수행하게된다.
    때문에, 수행하는 시간이 길어지게 되거나, list의 값이 클 경우에는 처음 실행 될 때 부담이 가게 된다.

  • generator_exp=
    generator를 사용하게 되면 list와는 다르게 하나씩 값을 불러오게되며, 한번에 모든 값이 실행되지 않기 때문에 연산시간이 오래 걸리는 경우에는 필요한 순간까지 늦출 수 있게 된다.

0개의 댓글