본문 바로가기

카테고리 없음

Python : Generator 완벽 가이드: 개념과 사용법

Generator란 무엇인가?

Python에서 generator는 특별한 종류의 함수로, 값을 하나씩 “yield”하며 이터레이터를 반환하는 기능을 합니다. 일반 함수와 달리 한 번에 하나씩 값을 반환하기 때문에, 모든 값을 메모리에 저장할 필요 없이 필요한 시점에만 값을 생성할 수 있습니다. 이는 특히 큰 데이터셋이나 데이터 스트림을 다룰 때 메모리를 효율적으로 관리하는 데 유용합니다. 

 

Generator의 주요 특징:

 

메모리 효율성: Generator는 항목을 한 번에 하나씩 생성하므로 리스트에 비해 메모리 효율적입니다.

무한 시퀀스: 자연수와 같은 무한 시퀀스 표현에 적합하며, 한 번에 모든 항목을 저장하려고 하지 않습니다.

상태 보존: 각 yield 문은 함수의 상태를 보존하여 다음 호출 시 이어서 진행할 수 있게 합니다.

Generator의 작동 방식

Generator는 일반 함수처럼 정의되지만, 값을 반환할 때  return 대신 yield 키워드를 사용합니다. Generator로 정의된 함수를 호출하면 함수가 즉시 실행되는 대신, generator 객체가 반환됩니다. 이 객체에서 값을 가져올 때마다 yield 지점에서 함수가 “일시 중지” 상태가 되고, 다음 호출 시 해당 지점부터 다시 실행을 이어갑니다.  아래에 간단한 예를 적어보았습니다.

def generator_ex01():
    print('start')
    yield 'A point'

    print('continue')
    yield 'B point'

    print('continue')
    yield 'c point'

 

위 함수를 처음 실행시키면 generator 객체가 반환됩니다.

gen1 = generator_ex01()
print('>>', type(gen1))

#Output:
#>> <class 'generator'>

 

Generator 는 반복 수행을 위해 Iterator를 반환하기 때문에, 다음 응답을 받기위해서는 iterator처럼 next() 함수를 사용하여 수행시키면 됩니다. 

print('>', next(gen1))
#Output:
#start
#>>A point 

print('>', next(gen1))
#Output:
#continue
#>>B point 

print('>', next(gen1))
#Output:
#continue
#>>C point

또는, for  루프를 사용하여 모두 출력할 수도 있습니다. 

gen1 = generator_ex01()
for i in gen1:
    print('>', i)

 

예제: 간단한 Generator

 

다음은 1에서 n까지의 숫자를 반환하는 간단한 generator 예제입니다:

def number_generator(n):
    for i in range(1, n + 1):
        yield i

# 1부터 5까지의 숫자 생성
for number in number_generator(5):
    print(number)

 

위 for 문에서 number_generator(5) 는 1에서 5까지의 숫자를 차례로 내보내는 iterator를 반환합니다. 이를 사용자 정의 iterator 객체로 구현하려면 __iter__와 __next__ 함수를 구현해야하는 다소 복잡한 구현절차가 필요한데 generator는 이를 단순화 시켜줍니다.  (물론 예제가 너무 쉬워서 리스트를 사용해도 되긴합니다.)

 

동일한 예제를 클래스로 구현해 본 예제입니다. 

class numberGenerator:
    def __init__(self,n) -> None:
        self._maxCount = n

    #iter 함수를 generator로 구현합니다.
    def __iter__(self):
        for i in range(1,self._maxCount+1):
            yield i

number_generator = numberGenerator(5)
for number in number_generator:
    print(number)

 

 __iter__ 함수를 generator로 구현하여 __next__ 함수를 구현하지 않아도 됩니다.

 

아래와 같이 튜플 형태로 Generator를 만들 수도 있습니다. 튜플처럼 보이지만 Generator입니다.

words = ["I","Can","Do","It"]   #iterable list
word_gen = ( w for w in words)  #create the generator 
print(type(word_gen))           #<class 'generator'>
print(next(word_gen))           #I
print(next(word_gen))           #Can
print(next(word_gen))           #Do
print(next(word_gen))           #It

 

Python generator는 특히 큰 데이터셋이나 무한 데이터 스트림을 처리할 때 효율적으로 시퀀스를 다룰 수 있는 방법을 제공합니다. yield를 사용하여 하나씩 값을 반환하는 generator는 메모리 사용량을 줄여주며 코드 효율성을 높입니다. 이는 Python이 추구하는 단순함과 가독성을 잘 반영한 기능으로, 메모리 친화적이며 세련된 방식으로 시퀀스를 처리할 수 있게 합니다.