# Lazy Evaluation과 Generator

# Generator

파이썬에서 사용되는 Generator를 이해하기 위해서는 우선 iterator에 대한 지식이 필요하다. 다음에 iterator에 대해서도 글을 쓰겠지만, 형태적인 차원에서 설명하자면 __iter____next__ 를 메소드로 가지고 있으면서, 호출된 후의 상태(state)를 저장하고 있다가 더 이상 내보낼 값이 없을 때 StopIteration 예외를 일으키는 클래스를 iterator라고 부른다.

듣기 만해도 복잡해 보인다. ㅡㅡ;

이렇듯 복잡한 iterator를 보다 쉽게 생성해주는 것이 바로 Generator 이다. 제네레이터를 통해 앞에서 언급한 기능과 메소드를 자동적으로 구현할 수 있는 것이다.

# Generator를 생성하는 방법

제네레이터를 생성하는 방법은 매우 간단하다. 그냥 함수에서 return 대신에 yield를 써주면 된다.

TIP

함수에 yield statement가 한 개 이상 있으면 Generator라고 부른다.

이 부분 때문에 Generator는 yield가 들어 있는 함수라고 이해하고 있는 사람들도 있다. 틀린말은 아니지만 정확히는 yield 는 iterator가 갖고 있는 여러 기능들을 보다 쉽게 구현해주록 파이썬에 내장된 statement이고 이를 통해서 간단하게 제네레이터를 만들 수 있다는 표현이 맞겠다(그리고 그렇게 간단하게 생성된 제네레이터를 통해서 iterator를 만들 수 있는 것, 그게 그말인가??)

def num_generator(n):
    while n>0:
        yield n
        n -= 1
    return 'Error Message goes here'

a = num_generator(3)

결과

>>> a.__next__()
>>> 3
>>> a.__next__()
>>> 2
>>> a.__next__()
>>> 1
>>> a.__next__()
>>> StopIteration: Error Message goes here

그래서 최종적으로 정리하자면 제네레이터는 다음과 같은 특징을 가지고 있다.

  • 한 개 이상의 yield statement를 갖는다.
  • 호출 되었을 때 iterator를 반환한다.
  • __iter__() __next__() 같은 함수들이 자동적으로 적용되어, <generator>.__next__() 와 같이 사용가능하다.
  • 제네레이터가 yield를 실행한 후에는 control을 caller에게 넘긴다. 즉 실행을 잠시 멈춘다.
  • 이 때 제네레이터 내부의 지역변수(local variable)들과 그 값들이 다시 호출 되기 전까지 기억되어 있다. -> 이게 어떻게 가능한 건지는 좀더 전체적인 아키텍쳐를 알게 되었을 때 설명할 수 있을 듯하다.
  • 그리고 함수가 실행을 마치면 StopIteration 예외가 발생한다.

# Lazy-evaluation(느긋한 계산법)

자 이제 제네레이터에 대해 어느정도 알아 보았으니, 제네레이터와 관련하여 알아두어야할 개념인 Lazy-evaluation 에 대해 알아보자. Lazy-evaluation 은 제네레이터의 특징 중 4번째 특징과 관련된다.

왜 Lazy 하다고 하느냐? yield를 하기 전에 계산을 완료해 놓지 않기 때문이다. 가령 위의 예시 코드에서 for 문은 한 번에 다 돌지 않고 a.__next__() 가 호출될 때만 돌게 된다. 만약에 일반적인 함수였다면 for 문은 한번에 다 돌았을 것이다.

이 차이는 [i for i in range(i)](i for i in range(i))의 차이를 통해 확인할 수 있다. 전자는 한번에 생성되는 리스트지만 후자는 호출될 때마다 for 문이 한 단계씩 진행되면서 i를 반환하는 제네레이터이다.

WARNING

그래서 어떤 함수의 실행 시간을 측정할 때 만약에 그 함수가 제네레이터라면 측정된 결과가 그 함수에 적용된 알고리즘 고유의 수행시간(시간복잡도)가 아닐 수 있다. Lazy-Evaluation이 적용되기 때문이다.

# 추가 질문

제네레이터와 이터레이터, 그리고 코루틴 모두 실행 중에 호출자에게 control을 넘기는 특징을 가지고 있다. 이때 피호출자의 지역변수와 그 값들이 남아 있는데 이게 어떻게 가능한 것인지 궁금하다. 아마 파이썬이라는 언어의 전체적인 아키텍쳐를 이해할 수 있을 때 설명할 수 있을 것 같다.

Last Updated: 3/14/2020, 3:33:02 AM