BetterWay30 제너레이터

김승환·2021년 7월 9일

코딩의 기술

목록 보기
1/36

리스트를 반환하기보다는 제너레이터를 사용하라

시퀀스를 결과로 만들어내는 함수를 만들 때 가장 간단한 선택은 원소들이 모인 리스트를 반환하는 것이다.

  • 예를 들어 문자열에서 찾은 단어의 인덱스를 반환하고 싶다고 하자
    • 아래 코드는 append 메서드를 이용해 리스트에 결과를 추가하고 함수 마지막에 리스트를 반환한다.

def index_words(text):
result = []
if text:
result.append(0)
for index, letter in enumerate(text):
print(index, end=" ")
print(letter)
if letter == ' ':
result.append(index + 1)
return result
address = '컴퓨터(영어: Computer, 문화어: 콤퓨터, 순화어:전산기)는 진공관'
result = index_words(address)
print(result[:10])
print(result)
0 컴
1 퓨
2 터
3 (
4 영
5 어
6 :
7
8 C
9 o
10 m
11 p
12 u
13 t
14 e
15 r
16 ,
17
18 문
19 화
20 어
21 :
22
23 콤
24 퓨
25 터
26 ,
27
28 순
29 화
30 어
31 :
32 전
33 산
34 기
35 )
36 는
37
38 진
39 공
40 관
[0, 8, 18, 23, 28, 38][0, 8, 18, 23, 28, 38]

문제점
코드에 잡음이 많음, 핵심을 알아보기 어렵다.
새로운 결과를 찾을 때마다 append를 호출한다.
메서드 호출이 너무 덩어리가 크기 때문에 리스트에 추가될 값의 중요성을 희석해버린다.(index+1)
결과 리스트를 만드는 줄과 결과를 반환하는 줄도 있다.
위 문제를 개선하기 위해 제너레이터 사용
yield식을 사용하는 함수에 의해 만들어진다.
제너레이터 함수가 실제로 실행되지 않고 즉시 이터레이터를 반환한다.
이터레이터가 next 내장 함수를 호출할 때마다 이터레이터는 제너레이터 함수를 다음 yield식까지 진행시킨다.
제너레이터가 yield에 전달하는 값은 이터레이터에 의해 호출하는 쪽에 반환된다.

#위와 동일한 예제
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
print(next(it))
print(next(it))
print(next(it))
print(next(it))
print(next(it))
print(next(it))
18
23
28

38


StopIteration Traceback (most recent call last)
in
3 print(next(it))
4 print(next(it))
----> 5 print(next(it))
6 print(next(it))

StopIteration:
반환하는 리스트와 상호작용하는 코드가 사라졌으므로 index_words_iter 함수가 훨씬 읽기 쉽다. 대신 결과는 yield식에 의해 전달된다. 제너레이터가 반환하는 이터레이터를 리스트 내장 함수에 넘기면 필요할 때 제너레이터를 쉽게 리스트로 변환할 수 있다.

result = list(index_words_iter(address))
print(result[:10])
[0, 8, 18, 23, 28, 38]
index_workds의 두 번째 문제점은 반환하기 전에 리스트에 모든 결과를 다 저장해야 한다는 것이다.
이로 인해 입력이 매우 크면 프로그램이 메모리를 소진해서 중단될 수 있다.
반면 함수를 제너레이터 버전으로 만들면 사용하는 메모리 크기를 어느정도 제한할 수 있으므로 입력 길이가 아무리 길어도 쉽게 처리할 수 있다.

ex) 다음은 파일에서 한 번에 한 줄씩 읽어 한 단어씩 출력하는 제너레이터를 정의한 코드

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, 6]
제너레이터를 정의할 때 한 가지 알아둬야 할 점
제너레이터가 반환하는 이터레이터에 상태가 있기 때문에 호출하는 쪽에서 재사용이 불가능하다.

profile
인공지능 파이팅!

0개의 댓글