ChangHyeon Nam's Blog notes and thoughts

리스트를 반환하기보다는 제너레이터를 사용하라

Comments

[Effective Python] Better way 30: 리스트를 반환하기보다는 제너레이터를 사용하라

  • 제네레이터를 사용하면 결과를 리스트에 합쳐서 반환하는 것보다 더 깔끔하다.
  • 제네레이터가 반환하는 이터레이터는 제네레이터 함수의 본문에서 yield가 반환하는 값들로 이뤄진 집합을 만들어낸다.
  • 제너레이터를 사용하면 작업메모리에 모든 입력과 출력을 저장할 필요가 없으므로 입력이 아주 커도 출력 시퀀스를 만들 수 있다.

  • 문자열에서 찾은 단어의 인덱스를 반환하는 함수를 만든다고 해보자. 다음 함수처럼 리스트를 반환하여 구현할 수 있다.

      def index_words(text):
          result = []
          if text:
              result.append(0)
          for index, letter in enumerate(text):
              if letter ==' ':
                  result.append(index+1)
          return result
    
      address = '가나다라 마바사 아자차카 타파하'
      result = index_words(address)
      print(result)
      # [0, 5, 9, 14]
    

    이 코드의 단점은 text의 크기가 매우 클 경우에, result라는 리스트에 모든 인덱스를 저장하고 있어야 한다는 것이다.

  • 위 코드를 제네레이터를 이용해 구현하면 메모리 상에서 위의 문제점이 없어진다.

      def index_words_iter(text):
          if text:
              yield 0
          for index, letter in enumerate(text):
              if letter ==' ':
                  yield index+1
    
      it = index_words_iter(address)
      print(next(it))
      print(next(it))
      # 0
      # 5
    

    제네레이터는 yield식을 사용하는 함수에 의해 만들어진다. 제네레이터 함수를 호출하면, 즉시 iterator를 반환한한다. 이터레이터가 next 내장함수를 호출하면 제네레이터 함수를 다음 yield식까지 진행 시킨다. 제네레이터가 yield까지 전달하는 값은 이터레이터에 의해 호출하는 쪽에 반환된다.

  • 리스트 내장함수에 제네레이터가 반환한 이터레이터를 넘기면 쉽게 리스트로 바꿀 수 있다.

      result = list(index_words_iter(address))
      print(result)
      # [0, 5, 9, 14]
    

    한가지 주의할 점은 제네레이터 함수를 호출한 쪽에서 반환받은 이터레이터에 대한 재사용은 불가능하다.