이미지 출처: 파이썬 코딩 도장
시퀀스를 결과로 만들어내는 함수를 만들 때 가장 간단한 방법은 원소들이 모인 리스트를 반환하는 것입니다.
예로 문자열에서 찾은 단어의 인덱스를 반환하고 싶을 떄, append
메서드를 사용해 리스트에 결과를 추가하고 함수 마지막에 리스트를 반환합니다.
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 = '컴퓨터(영어: Computer, 문화어: 콤퓨터, 순화어:전산기)는 진공관'
result = index_words(address)
print(result[:10])
[0, 8, 18, 23, 28, 38]
위 함수의 문제점
append
메서드 호출index + 1
) 의 중요성을 희석시킴이 함수를 개선하는 방법은 제너레이터를 ****사용하는 것!
def index_words_iter(text):
if text:
yield 0
for index, letter in enumerate(text):
if letter == ' ':
yield index + 1
it = index_words_iter(address)
print(next(it))
print(next(it))
0
8
위 함수가 호출되면 제너레이터 함수가 실제로 실행되지 않고 즉시 이터레이터를 반환합니다. 이터레이터가 next 내장 함수를 호출할 때마다 이터레이터는 제너레이터 함수를 다음 yield 식까지 진행시킵니다. 제너레이터가 yield에 전달하는 값은 이터레이터에 의해 호출하는 쪽에 반환됩니다.
위와 비교했을 때, 반환하는 리스트와 상호작용하는 코드가 사라져 index_words_iter
함수가 훨씬 읽기 쉽습니다.
대신 결과는 yield
에 의해 전달됩니다.
제너레이터가 반환하는 이터레이터를 리스트 내장함수에 넘기면 필요할 때 제너레이터를 쉽게 리스트로 반환할 수 있습니다.
result = list(index_words_iter(address))
print(result[:10])
[0, 8, 18, 23, 28, 38]
index_words
의 문제점으로 반환하기 전 모든 결과를 리스트에 저장해야한다는 점이 있었는데, 입력이 크다면 메모리 문제로 중단될 수 있습니다.
반면 같은 함수를 제너레이터로 만들 때, 메모리 크기를 제한할 수 있으므로 길이가 길어도 쉽게 처리 가능합니다.
아래 코드는 파일에서 한 번에 한 줄씩 읽어 한 단어씩 출력하는 제너레이터를 정의한 코드입니다.
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, 8, 18, 23, 28, 38]
제너레이터를 정의할 때 알아둬야 할 점은 제너레이터가 반환하는 이터레이터에 상태가 있기 때문에 호출하는 쪽에서 재사용이 불가능하다는 점입니다.
address.txt
: 컴퓨터(영어: Computer, 문화어: 콤퓨터, 순화어:전산기)는 진공관
정리