ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [2월 2주차-2/12(1)]이터레이터(Iterator)와 제너레이터(Generator) 쉽게 이해하기
    Why Not SW CAMP 5기/수업 기록 2025. 2. 12. 10:21

     

    Python에서 이터레이터(iterator)제너레이터(generator) 는 반복(iteration)을 효율적으로 처리하는 방식입니다.


    1️⃣ 이터레이터(Iterator)란?

    이터레이터는 한 번에 한 개의 요소만 가져올 수 있는 객체입니다.
    보통 for 문을 사용할 때 자동으로 동작하는데, 직접 만들 수도 있습니다.

    이터레이터의 특징

    • 한 번 사용한 값은 사라지고 다시 돌아갈 수 없음
    • for 문에서 자동으로 next()를 호출해 다음 값을 가져옴
    • iter()로 이터레이터 객체를 만들고, next()로 값을 하나씩 꺼낼 수 있음
    class MyIterator:
        def __init__(self, data):
            self.data = data
            self.position = 0  # 오타 수정 (positon → position)
            
        def __iter__(self):
            return self
        
        def __next__(self):
            if self.position >= len(self.data):
                raise StopIteration
            result = self.data[self.position]  # 오타 수정 (ppsition → position)
            self.position += 1
            return result
        
    if __name__ == '__main__':
        i = MyIterator([1, 2, 3])  # 리스트를 괄호가 아닌 대괄호로 감싸야 함
        for item in i:
            print(item)

    🔹 리스트와 이터레이터 비교

    numbers = [1, 2, 3, 4, 5]  # 리스트
    iterator = iter(numbers)   # 리스트를 이터레이터로 변환
    
    print(next(iterator))  # 1
    print(next(iterator))  # 2
    print(next(iterator))  # 3
    

    📌 next(iterator) 를 호출할 때마다 한 개씩 꺼냄
    📌 값이 없는데 계속 next()를 호출하면 StopIteration 오류 발생


    2️⃣ 제너레이터(Generator)란?

    제너레이터는 이터레이터를 더 쉽게 만들 수 있도록 도와주는 특수한 함수입니다.
    return 대신 yield를 사용해 한 번에 하나씩 값을 반환합니다.

    제너레이터의 특징

    • 메모리 효율적 (한 번에 한 개의 값만 생성해서 저장 X)
    • next()를 호출할 때마다 실행이 멈췄던 지점부터 다시 실행됨
    • 일반 함수처럼 def 키워드로 만들지만, yield를 사용함

    🔹 제너레이터 예제

    def my_generator():
        print("첫 번째 값 생성")
        yield 1
        print("두 번째 값 생성")
        yield 2
        print("세 번째 값 생성")
        yield 3
    
    gen = my_generator()  # 제너레이터 객체 생성
    
    print(next(gen))  # "첫 번째 값 생성" 출력 후 1 반환
    print(next(gen))  # "두 번째 값 생성" 출력 후 2 반환
    print(next(gen))  # "세 번째 값 생성" 출력 후 3 반환
    

    📌 yield가 값을 반환한 후 다음 next()가 호출될 때 그 다음 코드부터 실행


     

    4️⃣ 제너레이터의 실용 예제

    🔹 큰 데이터 처리 (메모리 절약)

    예를 들어, 1억 개의 숫자를 담는 리스트는 메모리를 많이 차지하지만, 제너레이터를 사용하면 필요할 때만 값을 생성합니다.

    def count_up_to(max_num):
        num = 1
        while num <= max_num:
            yield num
            num += 1
    
    gen = count_up_to(5)  # 1부터 5까지 생성하는 제너레이터
    
    for num in gen:
        print(num)  # 1, 2, 3, 4, 5 출력
    

    📌 한꺼번에 모든 숫자를 저장하지 않고 필요할 때마다 값을 생성

     

    🔹 제너레이터를 활용한 '시간이 오래 걸리는 작업' 처리

    import time
    
    def longtime_job():
        print('작업 수행 중...')
        time.sleep(1)
        return '완료'
    
    # 리스트 컴프리헨션 (즉시 실행)
    list_job = [longtime_job() for i in range(5)]  # 여기서 모든 작업이 실행됨
    print(list_job[0])  # '완료' (이미 수행된 결과 출력)
    

    📌 리스트 컴프리헨션([])은 모든 작업을 즉시 실행함 → 메모리 낭비 발생


    🔹 제너레이터를 활용한 '지연 실행' (Lazy Evaluation)

    list_job = (longtime_job() for i in range(5))  # 리스트가 아니라 제너레이터 객체 생성
    
    print(next(list_job))  # 첫 번째 작업 수행 (그때 실행됨)
    print(next(list_job))  # 두 번째 작업 수행
    

    📌 제너레이터 표현식(())을 사용하면 필요한 순간에만 실행됨 → 메모리 절약 가능


    5️⃣ 언제 사용하면 좋을까?

     이터레이터

    • 리스트, 튜플, 딕셔너리 같은 반복 가능한 객체를 직접 제어하고 싶을 때
    • 반복할 데이터가 이미 정해져 있을 때

     제너레이터

    • 대량의 데이터를 효율적으로 처리할 때 (예: 로그 파일 읽기, 데이터 스트림 처리)
    • 메모리를 절약하고 싶을 때 (예: 무한 숫자 생성, 대용량 데이터 처리)

     

    💡 결론

    이터레이터 제너레이터

    구현 방식 __iter__()와 __next__() 메서드를 구현 yield를 사용하여 함수 형태로 구현
    사용 방식 클래스로 직접 구현해야 함 함수로 간단히 구현 가능
    메모리 효율 모든 데이터를 메모리에 저장 필요할 때만 데이터를 생성 (메모리 절약)
    재사용성 한 번 반복 후 재사용 불가 (새로운 객체 필요) yield를 사용하여 중단된 부분부터 실행 가능
    예제 for 문에서 iter()와 next()를 직접 구현할 때 데이터 스트림, 대량 파일 읽기, API 요청 등에서 유용

    💡 이터레이터는 반복 가능한 객체를 만들 때 직접 구현하는 경우 사용되며,
    💡 제너레이터는 큰 데이터를 처리하거나 무거운 작업을 효율적으로 실행할 때 매우 유용합니다. 🚀

Designed by Tistory.