파이썬 이터레이터와 제너레이터

프로그래밍에서 순회 가능한 객체와 그 활용은 대규모 데이터 처리 시 필수적입니다. 파이썬은 이러한 작업을 수행하기 위해 이터레이터와 제너레이터라는 두 가지 강력한 도구를 제공합니다. 이번 글에서는 이터레이터와 제너레이터의 개념, 이들의 차이점, 그리고 사용법을 깊이 있게 다루어 보겠습니다.

이터레이터(Iterator)

이터레이터는 반복 가능한 객체를 나타내는 프로토콜로, 객체의 요소를 순회할 수 있는 인터페이스를 제공합니다. 파이썬에서 이터레이터는 __iter__() 메소드와 __next__() 메소드를 구현하여 만들어집니다. 이들은 반복문에서 순회를 수행할 때 자동으로 호출되며, 일반적으로 많은 양의 데이터를 처리할 때 유용합니다.

이터레이터의 작동 방식

이터레이터의 작동을 이해하기 위해서는 두 메소드에 대해 더 깊이 알아야 합니다.

  • __iter__()이터러블 객체를 반환합니다. 즉, 원래 객체 자신을 반환합니다. 이 메소드는 반복이 시작될 때 호출됩니다. 이터러블 객체는 시작 지점에서 이터레이터를 얻기 위해 사용됩니다.
  • __next__()이터레이션을 통해 데이터의 다음 값을 반환합니다. 만약 더 이상의 데이터가 없다면 StopIteration 예외를 발생시켜야 합니다. 이 메소드는 반복할 요소가 그룹화된 이터러블의 다음 항목을 가져오기 위해 호출됩니다.

간단한 이터레이터 예제

다음은 간단한 카운터 이터레이터의 예제 코드입니다:


class Counter:
    def __init__(self, low, high):
        self.current = low
        self.high = high

    def __iter__(self):
        return self

    def __next__(self):
        if self.current >= self.high:
            raise StopIteration
        else:
            self.current += 1
            return self.current - 1

counter = Counter(1, 5)
for number in counter:
    print(number)
    

위의 예제에서 Counter 클래스는 __iter__()와 __next__() 메소드를 구현하여 이터레이터 프로토콜을 따르고 있습니다. 이 클래스의 객체는 반복문(for loop)에서 사용할 수 있습니다.

제너레이터(Generator)

제너레이터는 이터레이터를 보다 간단히 생성할 수 있도록 도와주는 특별한 함수로, yield 키워드를 사용하여 값을 하나씩 반환합니다. 제너레이터는 호출되면 제너레이터 객체를 반환하며, 이는 제너레이터 함수가 값의 순회에 사용될 때 실행되고, 중지되었다가 다시 호출되면 중단된 지점에서 재개됩니다.

제너레이터의 작동 방식

제너레이터는 내부적으로 __iter__()와 __next__() 메소드를 자동으로 구현하여 사용자에게 이러한 구현을 숨깁니다. 따라서 제너레이터 함수를 호출하면 제너레이터 객체가 반환되며, 이 객체는 이터레이터처럼 사용할 수 있습니다.

제너레이터 예제

다음은 간단한 제너레이터 함수의 예제 코드입니다:


def simple_generator():
    yield 1
    yield 2
    yield 3

for value in simple_generator():
    print(value)
    

위의 예제에서, simple_generator() 함수는 호출될 때마다 yield 키워드를 사용하여 값을 하나씩 반환합니다. 이 제너레이터는 다른 이터레이터처럼 for 반복문에서 사용할 수 있습니다.

이터레이터와 제너레이터의 차이점

이터레이터와 제너레이터는 많은 유사점이 있지만, 몇 가지 중요한 차이점이 있습니다:

  • 구현의 간단함: 제너레이터는 yield 키워드를 사용하여 더 직관적이고 간단하게 구현할 수 있습니다. 이터레이터를 직접 작성할 때의 복잡함을 없앨 수 있습니다.
  • 상태 유지: 제너레이터는 상태를 자동으로 유지합니다. 제너레이터가 실행을 중단하고 있을 때 모든 현 상태를 기억하므로, yield를 계속해서 호출하면 그 상태를 지속적으로 유지합니다.
  • 메모리 사용: 제너레이터는 즉시 결과를 생성하지 않고, 필요할 때마다 하나씩 값을 생성하므로 메모리 효율적입니다. 이터레이터와 비교했을 때 대규모 데이터 처리에 더 유용합니다.

고급 사용 예제

제너레이터는 복잡한 로직과 결합되어 아주 효율적인 코드를 작성할 수 있습니다. 아래는 피보나치 수열을 제너레이터로 생성하는 예제입니다:


def fibonacci_generator():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib_gen = fibonacci_generator()
for _ in range(10):
    print(next(fib_gen))
    

이 예제에서 fibonacci_generator는 무한 피보나치 수열을 생성하며, for 반복문이나 next() 함수를 통해 필요한 만큼의 값을 출력할 수 있습니다.

실전에서의 활용

이터레이터와 제너레이터는 특히 대규모 데이터 스트림을 처리하거나, 결과의 전체 리스트를 메모리에 저장할 필요 없이 한 번에 하나씩의 값을 생성하여 메모리 사용을 최적화할 필요가 있는 상황에서 자주 사용됩니다.

파일 읽기: 파일의 각 줄을 제너레이터로 읽어들여 더 큰 파일을 메모리 효율적으로 처리할 수 있습니다.


def read_large_file(file_path):
    with open(file_path, 'r') as file:
        for line in file:
            yield line.strip()

for line in read_large_file("large_file.txt"):
    print(line)
    

결론

이터레이터와 제너레이터는 파이썬의 매우 강력한 기능이며, 이들을 사용하면 복잡하고 많은 양의 데이터 처리를 메모리 효율적이면서도 가독성 좋게 수행할 수 있습니다. 이 두 개념을 잘 이해하고 적절히 활용하면, 보다 효율적이고 확장 가능한 코드 작성을 할 수 있을 것입니다.

이 강좌가 파이썬 이터레이터와 제너레이터의 이해를 깊게 도와주었기를 바랍니다. 앞으로의 파이썬 프로그래밍 여정에서 이 내용을 활용해 보세요.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다