iterator 객체의 한 종류
제너레이터를 만드는 2가지 방법
- 제너레이터 함수: 제네레이터를 만들기 위한 함수 정의
- 제너레이터 표현식: 제너레이터를 만들기 위한 식
제너레이터의 최대 핵심이자 장점 -> lazy evaluation
제너레이터 함수
일반 함수와 다른 점은 yield
로 인해 제너레이터 함수가 된다는 점.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| def generator(): print('One!') yield 1 print('Two!') yield 2 print('Three!') yield 3
gen = generator()
print(next(gen)) print(next(gen)) print(next(gen)) print(next(gen))
""" One! 1 Two! 2 Three! 3 Traceback (most recent call last): File "test.py", line 14, in <module> print(next(gen)) StopIteration """
|
for문도 iterator을 이용하지만, Generator는 쓸때마다 next를 써야하는 불편함이 있기에 장점이 없다면 쓰고 싶지 않다. 그렇다면 써야하는 이유는 무엇일까?
결론적으로 말하자면 메모리 때문이다.
제너레이터를 사용하지 않았을 때]
1 2 3 4 5 6 7 8 9 10 11 12 13
| import sys
def pows(s): r = [] for i in s: r.append(i**2) return r
N = range(1,10) st = pows(N)
print(st) print(sys.getsizeof(st))
|
제너레이터 사용]
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import sys
def pows(s): for i in s: yield i**2
N = range(1,10) st = pows(N)
for i in st: print(i, end=' ')
print() print(sys.getsizeof(st))
|
메모리 사용량을 본다면 차이가 생겼음을 알 수 있다. 적은 데이터로도 저렇게 차이가 나는데 정말 많은 데이터를 가지고 한다면 큰 차이가 생길 것이다.
yield from
1 2 3
| def pows(s): for i in s: yield i
|
위의 for문을 간단하게 해결할 수 있는 방법이 있다.(파이썬 3.3이상 지원)
1 2
| def pows(s): yield from s
|
s에 함수를 넣어도 되지만, for문으로 i의 값에 제곱을 한다거나 변화하는 방법을 바로 적용하지 못하는 점은 다소 아쉽다.
제너레이터 표현식
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| def show(s): yield from s
s = [1,2,3,4,5]
res = show(s)
print(type(res))
for i in res: print(i, end=' ') print()
res = (i for i in range(1,6))
print(type(res))
|
제너레이터 표현식은 메모리를 효율적으로 사용할 수 있으며, 함수를 별도로 정의해야하는 불편함도 없다.
next()
을 써야하는 불편함(?)은 있다고 생각하는가? lazy evaluation의 장점을 버리지 말자.
제너레이터 표현식 사용 예시
1 2 3 4 5 6 7 8 9 10 11 12 13
| import sys
def sum(s): sumV = 0 for i in s: sumV += i return sumV
s = [i for i in range(1, 11)] res = sum(s) print(res)
print(sys.getsizeof(s))
|
위와 같이 쓰는 것을 제너레이터 표현식으로 사용하여 쓸 수도 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13
| import sys
def sum(s): sumV = 0 for i in s: sumV += i return sumV
s = (i for i in range(1,11)) res = sum(s) print(res)
print(sys.getsizeof(s))
|
원하는 값도 얻으면서 메모리도 줄일 수 있다.