generator

  • body 안에 yield
  • 파이썬 코루틴의 구현은 generator를 기반으로 한다.
  • 파이썬 stack frame이 heap에 저장되어 있기 때문에 가능하다.
  • return 값은 StopIteration이 발생했을 때 반환된다.
  • StopIteration도 객체이다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    def gen():
    print('gen start')
    yield 1
    print('abcde')
    yield 2
    print('fghjk')
    yield 3
    print('asdfasdf')
    yield 4
    print('123123123')
    return 'done'
    • 모든 제네레이터는 이터레이터이다.
    • 제네레이터는 게으른 팩토리이다.(값을 그 때 그 때 생성함)
    • 제네레이터를 호출했을 때 제네레이터 객체가 생성

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      g = gen()
      g
      >> <generator object gen at 0x001727F0>

      # 첫 번째 next() 호출하면 yield를 만날 때까지 함수가 실행이 된다.
      next(g)
      >> gen start
      1

      next(g)
      >> abcde
      2

      a = next(g)
      >> fghjk
      a
      >> 3

      next(g)
      >> asdfasdf
      4

      # 함수가 리턴을 만나면 StopIteration 객체가 생성
      # value에 지정해놓은 리턴 값을 반환한다.
      try:
      next(g)
      except StopIteration as exc:
      print(exc.value)
      pass
      >> 123123123
      done
    • coroutine function

    • 함수 실행 도중에 실행 주도권을 다른 함수에 넘길 수 있음
    • 내가 원하는 시점(non - preemptive)에 다시 실행 주도권을 가져올 수 있는 함수
    • 명시적(explicit)

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      # 제네레이터로 피보나치 만들기
      def fibo_gen(n):

      a = 0
      b = 1

      for _ in range(n):
      yield a
      a,b = b, a+b

      fibo = fibo_gen(15)

      for _ in range(15):
      print(next(fibo), end = " ")
      >> 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377
    • yield from

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      def gen():
      print('gen start')
      data1 = yield 1
      print(f'gen[1] : {data1}')
      data2 = yield 2
      print(f'gen[2] : {data2}')
      return 'END'

      # yield가 들어가있으면 generator
      # yield from 다음에는 generator 객체가 들어와야 함
      # yield from은 중계 역할
      # yield from 다음으로 나오는 generator 객체가 끝날 때까지 중계 역할
      # g = gen() 부분은 generator 객체이기 때문에 함수가 실행되지 않는다.
      # 만약 generator 객체가 아니고 일반 함수였다면 실행이 되야 한다.

      # delegate - 위임
      def delegate():
      g = gen()
      print('start')
      ret = yield from g
      print('end')
      print(f'return value : {ret}')
      return ret

      g = delegate()
      g
      >> <generator object delegate at 0x006C1430>

      first = g.send(None)
      >> start
      gen start

      second = g.send('world')
      >> gen[1] : world

      second
      >> 2

      # 'END'라는 generator g 객체의 리턴 값을 StopIteration 객체가 value에 넣음
      try:
      g.send('hello')
      except StopIteration as exc:
      print(exc.value)
      >> gen[2] : hello
      end
      return value : END
      END
Share