🗂️

4.7.6. 제너레이터

 

제너레이터

제너레이터란 이터레이터를 만드는 것을 말합니다. 보통 함수는 return문을 이용하여 반환하지만 제너레이터는 yield를 이용하여 반환합니다.
그렇다면 왜 제너레이터를 이용해서 이터레이터를 만들어야 할까요?
 
입력
#1번예제 for i in range(10): #2번예제 [i for i in range(10)]
 
예를 들어 보겠습니다. 0부터 10까지 출력하는 코드는 range를 활용하는 방법도 있지만 두번째 예제처럼 list로 변환하여 사용한다면 결과값은 같지만 선언과 즉시 메모리를 할당해야 하므로 메모리 효율성 측면에서 좋지 않습니다.
 
그러면 이렇게 한번 작성해보도록 할게요.
입력
def 숫자(): yield 0 yield 1 yield 2 yield 3 yield 4 yield 5 yield 6 yield 7 yield 8 yield 9 for i in 숫자(): print(i)
 
출력
0 1 2 3 4 5 6 7 8 9
위 예제에서 숫자()메서드는 range와 똑같은 역할을 하게됩니다. 순회를 하면서 다음 값만을 지칭하고 있다는 것입니다.
결과값도 똑같죠?
 
이번에는 while문을 이용해서 짝수만을 출력해보도록 하겠습니다.
입력
def 숫자(): x = 0 while x < 100: if x % 2 == 0: yield x x + = 1 for i in 숫자(): print(i)
 
출력
0 2 4 8 16 . . . 92 94 96 98
 
yield는 함수를 종료하는게 아니라 함수 바깥으로 값을 전달하는 역할을 합니다. 위 예제에서는 for문의 i로 숫자를 전달하는 것을 확인할 수 있습니다.
앞에서 배웠던 zip을 한 번 출력해볼게요.
 
입력
zip([1, 2, 3]. 'abc') #출력값이 제너레이터로 나옵니다. 이터레이터를 만들어주는 제너레이터 라고 합니다. list(([1, 2, 3]. 'abc')) #이렇게 해야 list형태로 나올 수 있어요.
 
출력
<zip at 0x214c76be488> [('1','a'),('2','b'),('3','c')]
 
마찬가지로 아까 만들었던 숫자()메서드를 list형태로 만들면 어떻게 될까요?
입력
def 숫자(종료값): x = 0 while x < 종료값: if x % 2 == 0: yield x x + = 1 list(숫자(10))
 
출력
[0, 2, 4, 6, 8]
 
값을 전달해주는 제너레이터이기 때문에 0부터 8까지를 가지게 되는 것을 확인할 수 있어요.
zip과 같은 형식인 filter, map을 해보도록 할게요.
 
입력
list(map(lambda x : x * 2, 'abc')) map(lambda x : x * 2, 'abc') list(filter(lambda x : x > 5, range(10))) filter(lambda x : x > 5, range(10))
 
출력
['aa', 'bb'. 'cc'] <map at 0x214c774c780> #이터레이터를 만들 수 있는 제너레이터가 출력되었습니다. [6, 7, 8, 9] <filter at 0x214c7774c940> #이터레이터를 만들 수 있는 제너레이터가 출력되었습니다.
 
그리고 다음 예제를 보도록 하죠.
입력
[i for i in range(10)] for i (i for in range(10))
 
출력
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] <generator object <genexpr> at 0x00000214c7771x58> #순회가능
 
[i for i in range(10)]형태를 sort에서도 보셨을거에요. sort도 제너레이터 형태가 있습니다.
 
입력
s = [i for i in range(10)] sorted(s) reversed(s) list(reversed(s))
 
출력
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] <list_reverseiterator at 0x214c7760320> [9, 8 ,7, 6, 5, 4, 3, 2, 1, 0]
reversed인데요, 이것을 list 형태로 변환 해주어야 원하는 출력값을 확인할 수 있을 것 입니다.