A A
[Python] Generator, Iterator, Yield (제너레이터, 이터레이터, Yield)

Generator (제너레이터)

제너레이터는 일련의 값을 생성하는 이터레이터의 일종으로, 함수처럼 정의되지만 값을 반환할 때 return 대신 yield 키워드를 사용합니다.
  • 제너레이터는 지연 평가(Lazy Evaluation)를 통해 필요한 시점에 값을 생성합니다.
  • 지연 평가: 필요한 시점에 값을 생성하여 메모리 효율성을 높입니다.
  • 상태 유지: 마지막 실행 지점에서 멈추고 상태를 기억하여 다음 호출 시 그 지점부터 재개합니다.
  • 메모리 효율성: 한 번에 하나의 값만 생성하므로 메모리 사용을 최소화합니다.
  • 코드 간결화: 복잡한 이터레이터 코드를 간단히 작성할 수 있습니다.
def my_generator():
    yield 1
    yield 2
    yield 3
    yield 4

for item in my_generator():
    print(item)
1
2
3
4

Iterator (이터레이터)

Iterator(이터레이터)는 __iter__() 와 __next__() 메서드를 구현한 객체입니다.
Iterator(이터레이터)는 반복 가능한 객체에서 값을 순차적으로 꺼내는 역할을 합니다.
  • 상태 유지: 이터레이터는 현재 위치를 기억하여 next() 호출 시 다음 값을 반환합니다.

Iterator(이터레이터) 구현 예시

  • 이터레이터를 직접 구현하려면 두 가지 메서드를 정의해야 합니다: __iter__()와 __next__().
class MyIterator:
    def __init__(self, data):
        self.data = data
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index < len(self.data):
            result = self.data[self.index]
            self.index += 1
            return result
        else:
            raise StopIteration

my_iter = MyIterator([1, 2, 3, 4])
for item in my_iter:
    print(item)
1
2
3
4

Generator (제너레이터) & Iterator (이터레이터) 차이점

1. 구현방식

  • Iterator(이터레이터): Class(클래스) 형태로 __iter__()와 __next__() Method를 구현합니다.
  • Generator(제너레이터): Function(함수) 형태로 yield 키워드를 사용합니다.

2. 사용 용의성

  • Iterator(이터레이터): 상대적으로 복잡한 구조입니다.
  • Generator(제너레이터): 간단한 코드로 구현 가능합니다.

3. 메모리 효율성

  • Iterator(이터레이터): 모든 값을 메모리에 저장합니다.
  • Generator(제너레이터): 값을 필요할 때마다 생성합니다.

Yield 키워드

yield 키워드는 Generator(제너레이터) 함수에서 값을 반환하고 함수의 실행 상태를 일시 중지하는 역할을 합니다.
  • 이는 return 키워드와 유사하지만, 함수를 종료하지 않고 실행 상태를 저장합니다.
  • 지연 평가: yield 키워드를 만나면 값을 반환하고 함수의 실행 상태를 저장합니다.
  • 상태 유지: 다음 호출 시 저장된 상태에서 다시 시작합니다.

 

필요성

메모리 절약

  • Generator(제너레이터)는 한 번에 하나의 값만 생성하므로 메모리 사용을 최소화할 수 있습니다.
def large_range():
    for i in range(1_000_000):
        yield i

# 메모리를 절약하면서 순차적으로 값을 생성
for value in large_range():
    if value > 10:
        break
    print(value)

 

복잡한 흐름 제어

  • 제너레이터를 사용하면 복잡한 반복 작업을 간단히 구현할 수 있습니다. 상태를 유지하면서 여러 단계의 작업을 처리할 수 있습니다.
def countdown(n):
    while n > 0:
        yield n
        n -= 1

for number in countdown(5):
    print(number)

Yield 키워드 활용 예시

대량 데이터 처리

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

for line in read_large_file('large_file.txt'):
    process(line)

 

대규모 데이터베이스 쿼리 결과 처리

def query_database(query):
    cursor = connection.execute(query)
    for row in cursor:
        yield row

for row in query_database('SELECT * FROM large_table'):
    process(row)

 

스트리밍 데이터 처리

실시간 데이터 스트리밍에서 제너레이터는 매우 유용합니다. 실시간 로그 파일을 처리할 때 사용할 수 있습니다.
def tail_file(file_path):
    with open(file_path, 'r') as file:
        file.seek(0, 2)  # 파일의 끝으로 이동
        while True:
            line = file.readline()
            if not line:
                time.sleep(0.1)  # 파일에 새로운 내용이 추가될 때까지 대기
                continue
            yield line

for line in tail_file('log.txt'):
    print(line)