인공지능 개발자 수다(유튜브 바로가기) 자세히보기

파이썬/파이썬 코딩

[파이썬] 제너레이터(Generators)와 이터레이터(Iterators)

Suya_03 2024. 3. 7. 20:51

목차

    반응형

    1. 설명

    • 제너레이터는 yield 키워드를 사용
    • 이 키워드는 함수의 실행을 일시 중지하고 값을 반환한 다음, 다음 호출 때 실행을 재개할 수 있게 합니다.
    • 제너레이터 함수는 호출될 때 함수 내의 코드를 실행하기 시작하는 것이 아니라, 이터레이터 객체를 반환합니다.
    • 이 이터레이터는 .next() 메서드를 사용하여 값을 요청할 때마다 제너레이터 함수 내의 코드를 실행합니다.

    2. 특징

    • 지연 실행
      • 제너레이터는 필요할 때까지 값을 계산하지 않습니다. 즉, 반복의 각 단계에서 다음 값이 필요할 때만 그 값을 생성합니다. 이러한 '지연 실행' 방식은 메모리 사용을 최적화하고, 특히 크거나 무한한 데이터 시퀀스를 다룰 때 유용합니다.
    • 상태 유지
      • 제너레이터는 현재 위치(상태)를 기억합니다. 즉, yield 키워드를 통해 값을 반환한 후에도, 다음에 next() 함수가 호출될 때 자신이 중단된 위치에서부터 실행을 재개합니다. 이 특성 덕분에 제너레이터는 복잡한 시퀀스나 상태 기반의 계산을 간단히 구현할 수 있게 해줍니다.
    • 메모리 효율성
      • 제너레이터는 모든 데이터를 메모리에 한 번에 로딩하지 않고, 한 번에 하나씩 값을 생성하기 때문에 메모리를 효율적으로 사용합니다. 이는 대용량 데이터를 처리할 때, 혹은 무한 데이터 시퀀스를 다룰 때 특히 중요합니다.
    • 단순성과 재사용성
      • 제너레이터를 사용하면 복잡한 반복 로직을 간단하고 재사용 가능한 컴포넌트로 캡슐화할 수 있습니다. 이는 코드의 가독성과 유지 보수성을 향상시킵니다.
    • 파이프라인 구성
      • 여러 제너레이터를 함께 체이닝(chain)하여 데이터 처리 파이프라인을 구성할 수 있습니다. 이 방법을 통해 데이터 변환, 필터링, 집계 등의 작업을 효율적으로 수행할 수 있습니다.

     

    2. 코드 예시

    2.1. 예시 1

    def simple_generator():
        yield 1
        yield 2
        yield 3
    
    # 제너레이터 사용
    for value in simple_generator():
        print(value)

     

    코드 설명

    • 이 예시에서 simple_generator 함수는 1, 2, 3 값을 차례대로 생성합니다. for 루프를 사용하면 제너레이터에서 생성된 각 값에 대해 반복할 수 있으며, 각 yield 문에 도달할 때마다 함수의 실행이 일시 중지되고 해당 값이 반환됩니다.

    2.2. 예시 2

    def simple_generator():
        yield 1
        yield 2
        yield 3
    
    # 제너레이터 생성
    gen = simple_generator()
    
    # next() 함수를 사용하여 제너레이터에서 다음 값을 수동으로 가져옴
    print(next(gen))  # 첫 번째 값인 1을 출력
    print(next(gen))  # 두 번째 값인 2를 출력
    print(next(gen))  # 세 번째 값인 3을 출력
    
    # 더 이상 가져올 값이 없으므로, 다음 호출에서 StopIteration 예외를 발생시킴
    try:
        print(next(gen))
    except StopIteration:
        print("Generator is exhausted. No more values to yield.")

    코드 설명

    • next(gen)을 호출하여 제너레이터에서 첫 번째 값을 요청하고 출력합니다. 이때 함수 내부의 코드가 첫 번째 yield 문까지 실행됩니다.
    • 같은 방식으로 next(gen)을 두 번째와 세 번째 호출하여 각각 다음 값들을 요청하고 출력합니다.
    • 모든 값이 소진되어 더 이상 yield할 값이 없을 때, next(gen) 호출은 StopIteration 예외를 발생시킵니다. 이 예외는 제너레이터의 끝을 나타냅니다.

     

    3. 활용

    제너레이터와 이터레이터는 파이썬에서 데이터 스트림을 효율적으로 처리하는 데 널리 활용됩니다. 여기 몇 가지 일반적인 활용 예시를 소개합니다.

     

    3.1. 무한 시퀀스 생성

    • 제너레이터는 무한한 데이터 시퀀스를 생성할 수 있으며, 이는 메모리에 모든 값을 저장할 필요가 없기 때문에 가능합니다. 예를 들어, 무한한 자연수 시퀀스를 생성하는 제너레이터를 구현할 수 있습니다.

    코드

    def infinite_integers():
        n = 0
        while True:
            yield n
            n += 1
    
    # 제너레이터 사용
    gen = infinite_integers()
    print(next(gen))  # 0
    print(next(gen))  # 1
    # 더 많은 값들을 필요에 따라 계속 가져올 수 있음

     

    실행결과

    0
    1

     

    3.2. 파일 처리

    • 큰 파일을 처리할 때, 파일의 모든 내용을 한 번에 메모리에 로드하는 대신 제너레이터를 사용하여 한 번에 한 줄씩 처리할 수 있습니다. 이는 메모리 사용량을 줄이고 효율성을 높입니다.

    코드

    def read_file_line_by_line(file_path):
        with open(file_path, 'r') as file:
            for line in file:
                yield line.strip()
    
    # 제너레이터 사용
    for line in read_file_line_by_line("large_file.txt"):
        print(line)

     

    3.3. 데이터 파이프라인

    • 제너레이터와 이터레이터는 데이터 처리 파이프라인에서도 유용합니다. 여러 데이터 처리 단계를 연결하여, 한 단계의 출력이 다음 단계의 입력으로 사용될 수 있습니다.

    코드

    def integers():
        for i in range(1, 10):
            yield i
    
    def squared(seq):
        for i in seq:
            yield i * i
    
    # 제너레이터 파이프라인
    for value in squared(integers()):
        print(value)

     

    3.4. 대규모 데이터셋 처리

    • 대규모 데이터셋을 분석하거나 처리할 때, 제너레이터를 사용하여 데이터의 일부분만 메모리에 로드하고 처리할 수 있습니다. 예를 들어, 대규모 데이터셋에서 특정 조건을 만족하는 항목만 필터링하여 처리하는 경우에 유용합니다.

    코드

    def filter_data(dataset, condition):
        for element in dataset:
            if condition(element):
                yield element
    
    # 대규모 데이터셋 처리 예
    # 데이터셋에서 조건을 만족하는 데이터만 필터링

     

    반응형