def index_words(text):
result = []
if text:
result.append(0)
for index, letter in enumerate(text):
if letter == ' ':
result.append(index + 1)
return result
address = "컴퓨터 영어 문화어 컴퓨터 전산기는 진공관"
result = index_words(address)
result
[0, 4, 7, 11, 15, 20]
잘 작동을 하나 index_words 함수에는 두가지 문제점이 존재한다.
이를 해결하기 위해 제너레이터를 사용하는 것이다.
제너레이터는 yield 식을 사용하는 함수에 의해 만들어진다.
def index_words_iter(text):
if text:
yield 0
for index, letter in enumerate(text):
if letter == ' ':
yield index + 1
이 함수가 호출되면 제너레이터 함수가 실제로 실행되지 않고 즉시 이터레이터를 반환한다.
이터레이터가 next 내장 함수를 호출할 때마다 이터레이터는 제너레이터 함수를 다음 yield 식까지 진행시킨다.
제너레이터가 yield에 전달하는 값은 이터레이터에 의해 호출하는 쪽에 반환된다.
it = index_words_iter(address)
print(next(it))
print(next(it))
0 4
제너레이터가 반환하는 이터레이터를 리스트 내장 함수에 넘기면 필요할 때 제너레이터를 쉽게 리스트로 변환할 수 있다.
result = list(index_words_iter(address))
print(result)
[0, 4, 7, 11, 15, 20]
두번째 문제는 반환하기 전에 리스트에 모든 결과를 다 저장해야 한다는 것이다.
이로 인해 입력이 매우 크면 프로그램이 메모리를 소진해서 중단될 수 있다.
반면 제너레이터는 메모리를 어느정도 제한할 수 있으므로 쉽게 처리할 수 있다.
def index_file(handle):
offset = 0
for line in handle:
if line:
yield offset
for letter in line:
offset += 1
if letter == ' ':
yield offset
이 함수의 작업 메모리는 입력 중 가장 긴 줄의 길이로 제한된다.
import itertools
with open('address.txt', 'r', encoding='utf-8') as f:
it = index_file(f)
results = itertools.islice(it, 0, 10)
print(list(results))
[0, 4, 7, 11, 15, 20]
제너레이터를 정의할 때 한가지 알아둬야 할 점이 있다.
제너레이터가 반환하는 이터레이터에 상태가 있기 때문에 호출하는 쪽에서 재사용이 불가능하다.