[Python] Generator

문성호·2020년 8월 24일

Generator란?

  • Generator란 Iterator를 생성하는 함수라고 표현할 수 있다.
  • 일반적인 함수는 값을 바로 반환하지만, Generator는 바로 반환하지 않고 양보할 수 있다. 아래 yield(양보)라는 개념을 통해 좀 더 자세히 이해할 수 있다.

yield와 Send

yield

  • 아래 예제 코드를 보자.
def number_generator():
    yield 0    # 0을 함수 바깥으로 전달하면서 코드 실행을 함수 바깥에 양보
    yield 1    # 1을 함수 바깥으로 전달하면서 코드 실행을 함수 바깥에 양보
    yield 2    # 2를 함수 바깥으로 전달하면서 코드 실행을 함수 바깥에 양보
 
g = number_generator()
 
a = next(g)    # yield를 사용하여 함수 바깥으로 전달한 값은 next의 반환값으로 나옴
print(a)       # 0
 
b = next(g)
print(b)       # 1
 
c = next(g)
print(c)       # 2
  • number_generator() 함수는 값을 return하지 않고 yield라는 키워드를 쓴 후
    g라는 Generator를 만들어 값을 순서대로 '산출'하고 있다.

  • 직관적으로 이해하자면, 함수의 반환값을 내가 원하는 순간에 '산출'할 수 있다라고 생각하면 쉽다.

  • 아래와 같이 내가 원할 때 함수의 결과값을 yield를 써서 순서대로 산출할 수 있다.

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

	gen = generator_squares()
	print(gen.__next__())    # 0
	print(gen.__next__())    # 1
	print(gen.__next__())    # 4

send

  • 일방적으로 제너레이터 함수에서 바깥으로 산출할 수 있을 뿐만 아니라, 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))
  • 제너레이터 함수에 입력하고자 하는 값을 gen.send(인자)의 방식을 통해 할당하고
    산출받을 수 있다.
  • 할당하고자 하는 자리에 yield를 써서 입력받고(received_value = yield),
    yield received_value * 2를 통해 2배 곱한 값을 산출할 수 있다.

Generator Expression

  • 리스트 컴프리헨션처럼 제네레이터도 간단하게 표현할 수 있다.
result = (x*x for x in L)
gen = result.__iter__()
print(gen.__next__())	# 1
print(gen.__next__())	# 4
print(gen.__next__())	# 9
  • 제네레이터 함수를 간단하게 표현한 식으로, 문법은 리스트 컴프리헨션과 같다.

Lazy Evaluation vs List Comprehension

import time

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)
  • 위의 코드를 실행해보면, List Comprehension으로 lazy_return함수를 호출했을 때는, lazy_return 함수를 3번 부른 후, print_iter함수를 호출했다.
    즉, comprehension_list가 다 끝날때까지 print_iter함수를 보류한다.

  • 그러나, generator expression을 사용한 아래 함수의 경우, iterator 객체에 변수를 호출할 때에만 lazy_return함수를 실행한다.

  • 결론적으로, Generator Expression을 사용할 때는 함수 호출을 필요할 때만 할 수 있기 때문에, 메모리 측면에서 훨씬 효율적이라고 볼 수 있다.

profile
오늘을 모아 내일을

0개의 댓글