Example
def return_abc():
return list("ABC")
위 함수를 yield 키워드를 이용해서 작성해볼까요?
def yield_abc():
yield "A"
yield "B"
yield "C"
for ch in return_abc():
print(ch)
A
B
C
for ch in yield_abc():
print(ch)
A
B
C
>>> print(return_abc())
['A', 'B', 'C']
>>> print(yield_abc())
<generator object yield_abc at 0x7f4ed03e6040>
Example
import time
def return_abc():
alphabets = []
for ch in "ABC":
time.sleep(1)
alphabets.append(ch)
return alphabets
for ch in return_abc():
print(ch)
# 3초 경과
A
B
C
위 함수를 호출한 결과를 for 루프로 돌려보면 3초가 후가 흐른 후에 A, B, C가 한 번에 출력 됨
yield 키워드를 이용해서 동일한 결과 값을 제공하는 함수를 작성
import time
def yield_abc():
for ch in "ABC":
time.sleep(1)
yield ch
for ch in return_abc():
print(ch)
# 1초 경과
A
# 1초 경과
B
# 1초 경과
C
위 함수를 호출한 결과를 for 루프로 돌려보면 1초 후에 A를 출력되고, 또 1초 후에 B를 출력되고, 또 1초 후에 C가 출력 됨
만약에 세개의 알파벳이 아닌 백개, 천개, 만개의 알페벳을 제공해야하는 경우에 첫번째 방식에서는 첫번째 결과값을 얻는데 백초, 천초, 만초가 걸리는 반면, 두번째 방식에서는 항상 일초가 걸림
제너레이터는 결과값을 나누어서 얻을 수 있어 성능 측면에서 큰 이점이 있음
메모리 효율 측면에서도 return 키워드는 모든 결과 값을 메모리에 올려놓아야 하는 반면, yield 키워드는 결과 값을 하나씩 메모리에 올려놓음
제너레이터(generator)는 이러한 특성 때문에 게으 반복자(lazy iterator)라고도 불리는데 이 특성을 잘 활용하면 좀 더 효율적인 프로그램을 작성할 수 있는 경우가 많음
특히 메모리에 한 번에 올리기에는 부담스럽게 대용량의 파일을 읽거나, 스트림 데이터를 처리할 때 상당히 유용하게 사용될 수 있음
Example
def yield_infinite_abc():
while True:
yield "A"
yield "B"
yield "C"
for ch in yield_infinite_abc():
print(ch)
A
B
C
A
B
C
A
B
C
A
^C
Traceback (most recent call last):
File "main.py", line 8, in <module>
print(ch)
KeyboardInterrupt
이 함수를 호출한 결과를 for 루프로 돌리면 A, B, C가 화면에 끊임없이 출력이 됨
Ctrl + C를 눌러서 빠져나올 수 있다.
데이터를 무한하게 제공하는 함수는 yield 키워드가 없이는 작성하는 것이 거의 불가능 함
컴퓨터의 물리적인 메모리에는 한계가 있으며 아무리 큰 리스트를 만들더라도 이 한계를 초과할 수는 없기 때문이다.
Example
def yield_abc():
for ch in ["A", "B", "C"]:
yield ch
def yield_abc():
yield from ["A", "B", "C"]
제너레이터를 만드는 또 다른 방법
리스트 표현식(list comprehension)과 사용 방법이 매우 유사하며 차이점은 리스트 표현식은 대괄호를 사용하고 제너레이터 표현식은 소괄호를 사용
abc = (ch for ch in "ABC")
print(abc)
for ch in abc:
print(ch)
<generator object <genexpr> at 0x7f2dab21ff90>
A
B
C