Generators

파이썬의 제너레이터(Generator)는 반복 가능한 객체를 생성하는 강력한 도구

제너레이터의 기능과 역할

  1. 메모리 효율성: 필요한 값만 생성하여 메모리를 절약합니다.
  2. 지연 평가: 필요할 때만 값을 생성하여 불필요한 연산을 피합니다.
  3. 무한 시퀀스 생성: 끝없는 데이터 스트림을 모델링할 수 있습니다.
  4. 복잡한 로직 간소화: 복잡한 반복 로직을 간단하게 표현할 수 있습니다.

제너레이터의 특징

  1. yield 키워드 사용: 함수 내에서 yield를 사용하여 값을 반환합니다.
  2. 상태 유지: 함수의 로컬 변수를 통해 내부 상태를 유지합니다.
  3. 이터레이터 프로토콜 준수: next() 함수를 통해 값을 하나씩 가져올 수 있습니다.
  4. StopIteration 예외: 모든 값을 생성한 후 StopIteration 예외를 발생시킵니다.

제너레이터의 주요 장점

  1. 메모리 효율성:

    • 모든 값을 한 번에 메모리에 저장하지 않고 필요할 때마다 생성
    • 대용량 데이터 처리에 적합
    • 무한 시퀀스 처리 가능
  2. 성능:

    • 지연 평가(lazy evaluation)로 필요한 만큼만 계산
    • I/O 작업의 효율적인 처리
    • 메모리 사용량 최소화
  3. 코드 가독성:

    • 복잡한 이터레이션 로직을 간단하게 표현
    • 데이터 파이프라인 구성이 용이
    • 비동기 프로그래밍과의 통합 가능

제너레이터의 종류와 예시

  1. 기본 제너레이터 함수
    제너레이터 함수는 yield 키워드를 사용하여 값을 하나씩 생성.
    일반 함수와 달리 상태를 유지하면서 값을 반환할 수 있다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    def number_sequence(start, end):
        """start부터 end까지의 숫자를 생성하는 기본 제너레이터"""
        current = start
        while current <= end:
            yield current
            current += 1
    
    for num in number_sequence(1, 5):
        print(num)  # 1, 2, 3, 4, 5가 순차적으로 출력
    
  2. 무한 시퀀스 제너레이터
    제너레이터는 무한한 시퀀스를 메모리 효율적으로 다룰 수 있다.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    def infinite_counter(start=0):
        """무한한 숫자 시퀀스를 생성하는 제너레이터"""
        current = start
        while True:
            yield current
            current += 1
    
    counter = infinite_counter()
    for _ in range(5):
        print(next(counter))  # 0, 1, 2, 3, 4가 출력
    
  3. 제너레이터 표현식
    리스트 컴프리헨션과 유사하지만 ()를 사용하여 제너레이터를 생성합니다.

    1
    2
    
    squares = (x**2 for x in range(5))
    print(list(squares))  # [0, 1, 4, 9, 16]
    
  4. 서브 제너레이터와 yield from
    yield from을 사용하여 다른 제너레이터의 값을 위임할 수 있습니다.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    def sub_generator(n):
        """0부터 n-1까지의 숫자를 생성하는 서브 제너레이터"""
        for i in range(n):
            yield i
    
    def main_generator(n):
        """서브 제너레이터를 사용하는 메인 제너레이터"""
        yield "시작"
        yield from sub_generator(n)
        yield "끝"
    
    for item in main_generator(3):
        print(item)  # "시작", 0, 1, 2, "끝" 순서로 출력
    
  5. 양방향 제너레이터
    send() 메서드를 사용하여 제너레이터에 값을 전달할 수 있습니다.

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    def echo_generator():
        """클라이언트로부터 값을 받아 에코하는 제너레이터"""
        while True:
            received = yield
            yield f"에코: {received}"
    
    echo = echo_generator()
    next(echo)  # 제너레이터 초기화
    echo.send("안녕")
    print(next(echo))  # "에코: 안녕" 출력
    

실무적 활용 사례

  1. 파일 처리
  2. 데이터 스트리밍
  3. 메모리 효율적인 데이터 처리
  4. 데이터 변환 파이프라인
  5. 성능 측정과 리소스 관리

제너레이터를 사용할 때 주의할 점

  1. 일회성 사용:

    • 제너레이터는 한 번 순회하면 소진됨
    • 재사용하려면 새로 생성해야 함
  2. 상태 관리:

    • 제너레이터는 내부 상태를 유지
    • 병렬 처리 시 주의가 필요
  3. 예외 처리:

    • 제너레이터 내부의 예외를 적절히 처리해야 함
    • StopIteration 예외의 이해 필요

참고 및 출처