프로그래밍 언어/Python

[Python] generator(제너레이터) 정리

benjykim 2021. 2. 24. 21:10
반응형

[Python] generator(제너레이터) 정리

Generator

  • Generator(제너레이터): iterator 를 생성해 주는 함수

    • iterator : next() 메서드를 이용해 데이터에 순차적으로 접근이 가능한 객체

  • generator 는 일반적인 함수와 비슷해보이지만, 가장 큰 차이점은 yield 라는 구문일 것이다. 아래의 예시를 보자

    def generator(n):
        i = 0
        while i < n:
            yield i
            i += 1

    일반 함수와의 차이는 yield 밖에 없다. yield는 무엇인가?

    • yieldgenerator가 일반 함수와 구분되는 가장 핵심적인 부분이다.

    • 일반적인 함수의 경우, 사용이 종료되면 결과 값을 호출부로 반환한 뒤 함수 자체를 종료시킨 후 메모리 상에서 정리(clear)된다.

    • 하지만, yield는 다르다. generator 함수가 실행 중에 yield를 만날 경우, 해당 함수는 그 상태로 정지되며, 반환 값을 next()를 호출한 쪽으로 전달한다. 이후 해당 함수는 일반적인 경우처럼 종료되는 것이 아니라 그 상태로 유지되게 된다. 즉, 함수에서 사용된 로컬 변수나 instruction pointer 등과 같은 함수 내부에서 사용된 데이터들이 메모리에 그대로 유지된다.

      def generator(n):
          i = 0
          while i < n:
              yield i
              i += 1
      
      for x in generator(5):
          print(x)
      
      # 출력
      0
      1
      2
      3
      4

      위 구문을 하나씩 살펴보자.

      1. for 문이 실행되며 generator 함수가 호출된다.
      2. generator 함수는 일반 함수와 동일한 절차로 실행된다.
      3. 실행 중 while 문 안에서 yield를 만나게 된다. 그러면 return 과 비슷하게 함수를 호출했던 구문으로 반환하게 된다. 여기서는 첫번째 i 값인 0을 반환한다. 하지만 반환했다고 generator 함수가 종료되는 것이 아니라 그대로 유지한 상태이다.
      4. x 값에는 yield에서 전달된 0 값이 저장된 후 출력된다. 그 후 for 문에 의해 다시 generator 함수가 호출된다.
      5. 이 때, generator 함수는 처음부터 시작되는 것이 아니라 yield 이후부터 시작된다. 따라서 "i += 1" 문장이 실행되고 i 값은 1로 증가한다.
      6. 아직 while 문 내부이기 때문에 yield 구문을 만나 i 값인 1이 전달된다.
      7. x 값은 1을 전달 받고 출력된다. (이후 반복)

Generator Expression

  • Generator Expression: generator 함수를 더 쉽게 사용하기 위해 제공되는 것

    • list comprehension과 비슷하지만 []대신 ()를 사용

    • 예제

      # 0 ~ 9 사이 정수 중 홀수를 list와 generator object로 생성
      >>> [i for i in xrange(10) if i % 2]
      [1, 3, 5, 7, 9]
      
      >>> (i for i in xrange(10) if i % 2)
      <generator object <genexpr> at 0x7f6105d90960>

      ()를 사용하면 위와 같이 generator object로 생성된다.


Generator 사용 이유

  1. 메모리를 효율적으로 사용할 수 있다.

    • 예제

      >>> import sys
      # list
      >>> sys.getsizeof( [i for i in xrange(100) if i % 2] )    
      536
      >>> sys.getsizeof( [i for i in xrange(1000) if i % 2] )
      4280
      
      # generator
      >>> sys.getsizeof( (i for i in xrange(100) if i % 2) )   
      80
      >>> sys.getsizeof( (i for i in xrange(1000) if i % 2) )
      80

      list의 경우 사이즈가 커질수록 메모리 사용량이 늘어남. 반면 generator의 경우 사이즈가 커져도 차지하는 메모리 사이즈는 동일함

      • 추가 설명
        • listlist 안에 속한 모든 데이터를 메모리에 적재하기 때문에 list의 크기만큼 차지하는 메모리 사이즈가 늘어나게 된다. 반면 generator는 데이터 값을 한꺼번에 메모리에 적재하는 것이 아니라 next() 메서드를 통해 차례로 값에 접근할 때마다 메모리에 적재한다. 따라서 list의 규모가 큰 값을 다룰수록 generator의 효율성은 높아진다.
  2. Lazy Evaluation 효과를 볼 수 있다.

    • Lazy Evaluation: 계산 결과 값이 필요할 때까지 계산을 늦추는 효과

    • 예제

      def sleep_fucn(x):
          print("sleep")
          time.sleep(1)
          return x

      sleep_func()는 1초간 sleep을 수행한 뒤 x 값을 반환하는 함수이다. 만약 위 sleep_func()함수를 이용해 listgenerator를 생성하면 어떻게 동작할까?

      • list 예제
      list = [sleep_func(x) for x in xrange(5)]
      for i in list:
          print(i)
      
      # 출력
      sleep...
      sleep...
      sleep...
      sleep...
      sleep...
      0
      1
      2
      3
      4
      • generator 예제
      gen = (sleep_func(x) for x in xrange(5))
      for i in gen:
          print i
      
      # 출력
      sleep...
      0
      sleep...
      1
      sleep...
      2
      sleep...
      3
      sleep...
      4

      list의 경우, list의 모든 값을 먼저 수행하기 때문에 sleep_func() 함수를 xrange() 값만큼 한번에 수행한다. 만일 sleep_func()에서 수행하는 시간이 길거나 list값이 매우 큰 경우 처음 수행할 때 그만큼 부담으로 작용된다.


      반면, generator의 경우 generator 생성 시 실제 값을 로딩하지 않고, for 문이 수행될 때 하나씩 sleep_func()를 수행하며 값을 불러오게 된다. 수행 시간이 긴 연산을 필요한 순간까지 늦출 수 있다는 점이 특징이다.


      이러한 특징을 이용하면, fibonacci 수열과 같은 작업을 간결한 문법과 더불어 매우 효율적으로 코드를 작성할 수 있다.

      def fibonacci_func(n):
          a,b = 0, 1
          i = 0
          while True:
              if (i > n):    return
              yield a
              a, b = b, a+b
              i += 1
      
      fib = fibonacci_func(10)
      for x in fib:
          print(x)

반응형