본문 바로가기
개발 언어/Python

파이썬 고급 2강: 게으른 평가 (Lazy Evaluation): 제너레이터와 이터레이터 활용

by 주호파파 2025. 5. 26.
728x90
반응형

 

2

파이썬 고급편 시리즈

Advanced Python Programming

게으른 평가 (Lazy Evaluation)
제너레이터와 이터레이터 활용

메모리 효율적인 파이썬 프로그래밍의 핵심 개념을 마스터해보세요

 

개요 및 중요성

이번 강의에서는 파이썬의 제너레이터(Generator)이터레이터(Iterator)에 대해 깊이 있게 다룹니다. 이들은 파이썬에서 메모리 효율적인 프로그래밍을 가능하게 하는 핵심 개념입니다.

특히 대용량 데이터를 다루거나 무한 시퀀스를 생성할 때, 제너레이터와 이터레이터를 활용하면 메모리 사용량을 극적으로 줄이면서도 효율적인 데이터 처리가 가능합니다.

💡 핵심 포인트

게으른 평가(Lazy Evaluation)를 통해 필요한 시점에만 값을 생성하여 메모리와 성능을 최적화할 수 있습니다.

 

이터레이터 프로토콜 이해하기

파이썬의 이터레이터 프로토콜은 __iter__()__next__() 메서드로 구성됩니다. 이를 구현하면 객체를 반복 가능하게 만들 수 있습니다.

 
 
 
이터레이터 클래스 구현

class CountDown:
    """지정된 숫자부터 0까지 카운트다운하는 이터레이터"""
    
    def __init__(self, start):
        self.start = start
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.start <= 0:
            raise StopIteration
        self.start -= 1
        return self.start + 1

# 사용 예시
countdown = CountDown(5)
for num in countdown:
    print(num)  # 5, 4, 3, 2, 1 출력
                    

위 코드에서 __iter__()는 이터레이터 객체를 반환하고, __next__()는 다음 값을 반환하거나 더 이상 값이 없으면 StopIteration 예외를 발생시킵니다.

 

제너레이터와 yield 키워드

제너레이터는 yield 키워드를 사용하는 함수로, 이터레이터보다 훨씬 간단하게 구현할 수 있습니다. 함수가 호출될 때마다 이전 상태를 기억하고 있다가 다음 값을 생성합니다.

 
 
 
제너레이터 함수 구현

def countdown_generator(start):
    """yield를 사용한 간단한 카운트다운 제너레이터"""
    while start > 0:
        yield start
        start -= 1

# 사용 예시
gen = countdown_generator(5)
for num in gen:
    print(num)  # 5, 4, 3, 2, 1 출력

# 무한 시퀀스 제너레이터
def infinite_numbers():
    """무한히 증가하는 숫자를 생성"""
    num = 0
    while True:
        yield num
        num += 1
                    

🚀 제너레이터의 장점

  • • 메모리 효율성: 모든 값을 메모리에 저장하지 않음
  • • 지연 평가: 필요한 시점에만 값을 계산
  • • 무한 시퀀스 생성 가능
  • • 간단한 구문으로 복잡한 이터레이션 로직 구현
 

메모리 효율성과 성능 비교

리스트 컴프리헨션과 제너레이터 표현식의 메모리 사용량 차이를 살펴보겠습니다. 큰 데이터셋을 다룰 때 이 차이는 매우 중요합니다.

 
 
 
메모리 사용량 비교

import sys

# 리스트 컴프리헨션 - 모든 값을 메모리에 저장
list_comp = [x * x for x in range(1000000)]
print(f"리스트 메모리 사용량: {sys.getsizeof(list_comp):,} bytes")

# 제너레이터 표현식 - 값을 필요할 때마다 생성
gen_expr = (x * x for x in range(1000000))
print(f"제너레이터 메모리 사용량: {sys.getsizeof(gen_expr):,} bytes")

# 출력 예시:
# 리스트 메모리 사용량: 8,448,728 bytes
# 제너레이터 메모리 사용량: 104 bytes
                    

🔴 리스트 컴프리헨션

  • • 모든 값을 한 번에 메모리에 로드
  • • 빠른 인덱스 접근 가능
  • • 큰 데이터셋에서 메모리 부족 위험

🟢 제너레이터 표현식

  • • 필요할 때마다 값을 생성
  • • 매우 적은 메모리 사용
  • • 무한 시퀀스 처리 가능
 

itertools 모듈 활용

파이썬의 itertools 모듈은 효율적인 반복을 위한 강력한 도구들을 제공합니다.

 
 
 
itertools 활용 예시

import itertools

# count: 무한 산술 수열
counter = itertools.count(10, 2)  # 10부터 2씩 증가
for i, num in enumerate(counter):
    if i >= 5:
        break
    print(num)  # 10, 12, 14, 16, 18

# cycle: 무한 반복
colors = itertools.cycle(['red', 'green', 'blue'])
for i, color in enumerate(colors):
    if i >= 7:
        break
    print(f"항목 {i}: {color}")

# chain: 여러 이터러블 연결
list1 = [1, 2, 3]
list2 = [4, 5, 6]
chained = itertools.chain(list1, list2)
print(list(chained))  # [1, 2, 3, 4, 5, 6]
                    
 

실습: 피보나치 수열 제너레이터

이번 강의에서 학습한 내용을 바탕으로 피보나치 수열을 생성하는 메모리 효율적인 제너레이터를 구현해보겠습니다. 이 예제는 제너레이터의 상태 보존과 지연 평가의 특성을 잘 보여줍니다.

 
 
 
fibonacci_generator.py

def fibonacci_generator(n=None):
    """
    피보나치 수열을 생성하는 제너레이터
    
    Args:
        n (int, optional): 생성할 피보나치 수의 개수. 
                          None이면 무한히 생성
    
    Yields:
        int: 피보나치 수
    """
    a, b = 0, 1
    count = 0
    
    while n is None or count < n:
        yield a
        a, b = b, a + b
        count += 1

def fibonacci_list(n):
    """비교를 위한 일반 함수 (리스트 반환)"""
    result = []
    a, b = 0, 1
    for _ in range(n):
        result.append(a)
        a, b = b, a + b
    return result

if __name__ == "__main__":
    print("=== 제너레이터 사용 ===")
    fib_gen = fibonacci_generator(10)
    print("처음 10개의 피보나치 수:")
    for i, fib in enumerate(fib_gen):
        print(f"F({i}) = {fib}")
    
    print("\n=== 메모리 사용량 비교 ===")
    import sys
    
    fib_gen_large = fibonacci_generator(1000)
    fib_list_large = fibonacci_list(1000)
    
    print(f"제너레이터 메모리: {sys.getsizeof(fib_gen_large)} bytes")
    print(f"리스트 메모리: {sys.getsizeof(fib_list_large):,} bytes")
                    

코드 실행 방법 및 결과

위 코드를 실행하면 다음과 같은 결과를 확인할 수 있습니다:

=== 제너레이터 사용 ===
처음 10개의 피보나치 수:
F(0) = 0
F(1) = 1
F(2) = 1
F(3) = 2
F(4) = 3
F(5) = 5
F(6) = 8
F(7) = 13
F(8) = 21
F(9) = 34
=== 메모리 사용량 비교 ===
제너레이터 메모리: 104 bytes
리스트 메모리: 9,112 bytes
 

마무리

이번 강의에서는 파이썬의 제너레이터와 이터레이터를 통한 게으른 평가(Lazy Evaluation)의 핵심 개념을 다뤘습니다. yield 키워드를 사용한 제너레이터는 메모리 효율성과 성능 최적화에 큰 도움이 됩니다.

🎯 핵심 요약

  • 이터레이터 프로토콜: __iter__()__next__() 메서드로 반복 가능한 객체 구현
  • 제너레이터: yield 키워드로 간단하게 이터레이터 생성
  • 메모리 효율성: 필요한 시점에만 값을 생성하여 메모리 사용량 최소화
  • 무한 시퀀스: 메모리 제한 없이 무한한 데이터 스트림 처리 가능
  • itertools 모듈: 고급 이터레이션 패턴을 위한 유용한 도구들

다음 강의에서는 컨텍스트 매니저(Context Managers)with 구문을 통한 리소스 관리의 효율화에 대해 학습하겠습니다. 파일 처리, 네트워크 연결 등에서 안전하고 깔끔한 코드 작성법을 알아보세요!

728x90
반응형