ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python] 이터레이터와 제너레이터, 그리고 파이썬의 지연 평가
    IT 2026. 1. 21. 15:34

     

    파이썬에서 제너레이터까지 어느 정도 익히고 나면, 자연스럽게 이런 질문들이 이어집니다.

    "이터레이터와 제너레이터는 정확히 뭐가 다른 걸까?"
    "iter와 next는 도대체 왜 직접 호출할 수 있게 만들어 둔 걸까?"
    "그리고 파이썬은 왜 이렇게까지 지연 평가(lazy evaluation)를 중요하게 생각했을까?"

    이 질문들은 사실 모두 하나의 큰 흐름으로 연결되어 있습니다. 이 글에서 각각을 따로 떼어 설명하기보다는, 파이썬이 데이터를 순회하는 방식을 어떻게 설계했는지라는 관점에서 하나의 이야기처럼 풀어보려 합니다.

     

    파이썬에서 반복이란 무엇일까?

    우리는 너무 자연스럽게 for 문을 사용하지만, 사실 파이썬 for문은 생각보다 많은 일을 내부에서 처리하고 있습니다.

    for x in data:
        print(x)

    이 코드는 단순히 리스트를 순회하는 것처럼 보이지만, 파이썬은 내부적으로 다음과 같은 질문을 던집니다.

    "이 객체를 하나씩 꺼내 쓸  수 있는가?"

    이 질문에 대한 파이썬의 기준이 바로 이터레이터(iterator) 입니다.

     

    이터레이터란 무엇인가

    이터레이터란 아주 단순하게 말하면, 다음 값을 하나씩 내놓을 수 있는 객체입니다. 그리고 파이썬에서 이터레이터는 두 가지 약속을 반드시 지켜야 합니다.

    • __iter__() 메서드를 가지고 있을 것
    • __next__() 메서드를 가지고 있을 것

    이 두 가지 약속만 지키면, 그 객체는 파이썬에서 순회 가능한 대상이 됩니다.

    numbers = [1, 2, 3]
    it = iter(numbers)
    
    print(next(it))
    print(next(it))
    print(next(it))

    우리가 평소에 사용하는 for문은, 사실 상 iter와 next를 대신 호출해 주는 문법적 장치에 가깝습니다.

     

    iter와 next의 정체

    그렇다면 iter와 next는 왜 굳이 사용자에게 노출되어 있을까요?

    그 이유는 파이썬이 반복의 동작을 아주 명시적인 프로토콜로 정의했기 때문입니다. 반복이 마법처럼 동작하는 것이 아니라, 명확히 규칙위에서 돌아가도록 설계된 것입니다.

    • iter(obj) -> 이 객체를 순회 가능한 형태로 바꿔 달라
    • next(it) -> 다음 값을 하나 달라

    이 단순한 규칙 덕분에, 리스트뿐 아니라 파일 객체, 제너레이터, 심지어 우리가 직접 만든 객체까지도 모두 같은 방식으로 순회할 수 있습니다.

     

    iter와 next의 정체

    여기서 중요한 사실 하나를 짚고 넘어가야 합니다.

    모든 제너레이터는 이터레이터다. 하지만 모든 이터레이터가 제너레이터는 아니다.

    제너레이터는 이터레이터를 아주 쉽게 만들 수 있게 해주는 도구라고 이해하는 편이 훨씬 자연스럽습니다.

    def gen():
        yield 1
        yield 2
        yield 3
    
    
    g = gen()
    print(next(g))
    print(next(g))

    이 g 객체는 이미 __iter__와 __next__를 모두 구현하고 있습닏. 다만 그 구현을 우리가 직접 하지 않았을 뿐입니다. 파이썬이 대신 만들어 준 것입니다.

     

    yield from은 왜 필요했을까?

    제너레이터를 조금 더 쓰다 보면, 이런 상황을 마주하게 됩니다.

    "제너레이터 안에서 또 다른 제너레이터를 순회하고 싶다"

    가장 직관적인 방법은 이런 형태일 것입니다.

    def generator_a():
        yield 1
        yield 2
    
    
    def generator_b():
        for x in generator_a():
            yield x
        yield 3

    이 코드는 문제없이 동작합니다. 하지만 파이썬은 여기서 한 발 더 나아가, 이 패턴을 더 명확하게 표현할 수 있는 문법을 제공합니다.

    def generator_b():
        yield from generator_a()
        yield 3

    yield from 은 단순한 축약 문법이 아닙니다. 이 문법은 "이 반복을 그대로 위임한다"라는 의도를 아주 분명하게 드러냅니다.

     

    yield from이 가지는 의미

    yield from 은 다음과 같은 일을 한 번에 처리합니다.

    • 내부 이터레이터의 값을 하나씩 바깥으로 전달
    • 내부 이터레이터가 끝날 떄까지 책임지고 순회
    • 중간에 불필요한 코드 개입을 제거

    즉, 제너레이터를 조합 가능한 구조로 만들어 주는 도구입니다. 이는 파이썬이 제너레이터를 단순한 반복 도구가 아니라, 하나의 데이터 흐름으로 바라보고 있다는 증거이기도 합니다.

     

    파이썬은 왜 지연 평가를 선택했을까?

    이제 마지막 질문으로 돌아와 보겠습니다.

    "파이썬은 왜 이렇게까지 지연 평가를 중요하게 설계했을까?"

    그 이유는 크게 세 가지로 정리할 수 있습니다.

    첫째, 메모리 효율입니다. 모든 데이터를 한 번에 메모리에 올리는 것은 비효율적이며, 때로는 불가능합니다.

    둘째, 표현력입니다. 제너레이터와 이터레이터를 사용하면 데이터 처리 과정을 파이프라인처럼 표현할 수 있습니다.

    셋째, 확장성입니다. 지연 평가는 무한한 데이터 스트림이나, 끝을 알 수 없는 반복을 자연스럽게 다룰 수 있게 해줍니다.

     

    파이썬은 전통적으로 "필요한 만큼만 하자"라는 철학을 가지고 있습니다. 제너레이터와 이터레이터는 이 철학이 반복 구조에 그대로 반영된 결과물이라고 볼 수 있습니다.

    모든 것을 미리 계산하지 않고, 요청이 들어올 때만 조금씩 처리하는 방식은 코드의 책임을 분명하게 만들고, 불필요한 낭비를 줄여줍니다.

     

     

    마무리하며

    이터레이터, 제너레이터, yield from, 그리고 지연 평가는 각각 따로 떨어진 개념처럼 보이지만, 사실은 모두 하나의 질문에서 출발합니다.

    "데이터를 우리는 어떻게 순회해야 할까?"

    파이썬은 이 질문에 대해, 명시적이고, 효율적이며, 조합 가능한 방식이라는 답을 선택했습니다. 그 결과가 바로 지금 우리가 사용하는 이터레이터와 제너레이터 구조 입니다.

    이 흐름을 이해하고 나면, for 문 하나조차도 이전과 조금 다르게 보이기 시작할 것입니다.

     

    여기서 마치겠습니다.

    감사합니다.

Designed by Tistory.