-
이터레이터(iterator) 개념
예전 글에서 for문을 설명할 때 이터레이터라는 단어를 사용한적이 있는데, 이터레이터는 list, tuple, range과 같은 시퀀스 객체의 요소를 하나씩 꺼낼 수 있는 객체에서 사용 가능 합니다. (시퀀스 객체 말고도 딕셔너리,세트도 반복 가능한 객체이기 때문에 이터레이터가 가능 합니다)
# 이터레이터를 확인하기 위해 간단한 리스트 생성
test = [10, 20, 30]
# __iter__() 메서드를 사용하여 이터레이터를 쓸 수 있도록 설정
test_iter = iter(test)
# 또는 test_iter = test.__iter__()
# __next__() 메서드를 사용하여 요소를 하나씩 불러옴
print(test_iter.__next__())
print(test_iter.__next__())
print(test_iter.__next__())
print(test_iter.__next__())
결과값 : 10
결과값 : 20
결과값 : 30
결과값 : StopIteration 에러 발생
__iter__ 메소드를 사용하여 이터레이터를 사용할 수 있도록 정의하고난 후 __next__ 메소드를 하여 요소를 하나씩 출력 합니다. 요소는 총 3개이므로 마지막 4번째의 __next__는 더이상 꺼내올 요소가 없기에 StopIteration 에러를 발생 시킵니다. (iter와 next말고도 __getitem__도 있습니다)
또는 아래 코드처럼 테스트해볼 수 있습니다.
test = [10, 20, 30]
test_iter = iter(test);
test_next = next(test_iter); print(test_next)
test_next = next(test_iter); print(test_next)
test_next = next(test_iter); print(test_next)
test_next = next(test_iter); print(test_next)
결과값 : 10
결과값 : 20
결과값 : 30
결과값 : StopIteration 에러 발생
-
제너레이터(generator) 개념 (yield, yield from)
제너레이터는 이터레이터를 생성해주는 함수인데, 가장 큰 차이점은 제너레이터는 yield를 사용한다는 것과 메모리를 적재하는 방식에 있어서 차이가 납니다. yield from은 요소를 하나씩 꺼내서 밖으로 전달해줍니다.
def gen():
yield 10
yield 20
yield 30
test_gen = gen()
n = next(test_gen); print(n)
n = next(test_gen); print(n)
n = next(test_gen); print(n)
n = next(test_gen); print(n)
결과값 : 10
결과값 : 20
결과값 : 30
결과값 : StopIteration 에러 발생
위의 코드 실행 결과와 같이 제너레이터는 이터레이터를 생성하고 동작 시키는데, 결과로만 보면 똑같아 보이지만 이터레이터와 제너레이터의 가장 큰 차이점은 메모리를 적재하는 방식 입니다.
우선 간단한 yield from을 사용하여 요소를 하나씩 꺼내서 밖으로 전달해주는지 확인 후 아래에 이터레이터와 제너레이터의 차이를 작성 하겠습니다.
def gen():
x = [10, 20, 30]
yield from x
test_gen = gen()
n = next(test_gen); print(n)
n = next(test_gen); print(n)
n = next(test_gen); print(n)
n = next(test_gen); print(n)
결과값 : 10
결과값 : 20
결과값 : 30
결과값 : StopIteration 에러 발생
-
이터레이터와 제너레이터의 차이
단순히 yield를 쓰냐 안쓰냐의 차이도 있지만 가장 큰 차이점은 이터레이터는 모든 동작을 완료한 후 결과를 한번에 메모리 적재 시키는것에 반해, 제너레이터는 각각의 yield에서 한번 실행 시킨 후 대기 상태에 들어가 결과를 반환, 이후 다음 코드를 진행하여 또다시 yield를 만날 경우 대기 상태에 들어가 결과를 반환하는 방식입니다.
# 이너레이터
def test_iter():
start = 1
x = iter(range(1, 4))
for i in x:
print(i)
test_iter()
아래는 제너레이터의 코드와 실행 흐름 입니다. yield가 호출될때마다 함수가 잠시 대기 상태에 들어가고 결과값을 출력 후 다시 함수가 실행 상태로 되돌아 갑니다. 이렇게 yield를 만날때마다 함수는 잠시 대기 상태에 들어가고 바깥에 값을 전달하며 실행 순서를 양보 합니다.
# 제너레이터
def gen(count):
start = 1
while start <= count:
yield start
start += 1
for i in gen(3):
print(i)
아래는 제너레이터의 실행 흐름 입니다. 빨간색 화살표 → 파란색 화살표 순으로 보시면 됩니다.
아래 코드처럼 time.sleep을 이용하여 확실히 밖에 값을 전달하고 출력 후 함수로 되돌아온다는걸 알 수 있습니다. 아래 코드를 실행하면 1 출력 후 2초간 대기, 2 출력 후 2초간 대기, 3 출력 후 2초간 대기가 되는데, 함수엔 결과를 yield를 이용해 밖으로 보내주기만하고 출력하는 기능은 없습니다.
즉, yield를 이용해 값을 함수 밖으로 보내고 대기 상태에 들어간 후 실행 순서를 양보하여 함수 밖의 print(i)가 출력 후 실행 순서가 함수로 다시 되돌아온다는 것을 알 수 있습니다.
import time
def gen(count):
start = 1
while start <= count:
yield start
time.sleep(2) # yield start 실행 후 2초간 대기
start += 1
for i in gen(3):
print(i)
'Programming > Python' 카테고리의 다른 글
[Python 2.7] pip 사용 시 bash: pip: command not found 에러 발생 (1) | 2020.06.17 |
---|---|
[Python 3.7] 간단한 파이썬 네이버 영화 평점 크롤링 만들기 (네티즌 평점 상위 5개 크롤링) (6) | 2020.04.02 |
[Python 3.7] 파이썬 예외 처리 (try, except, else, finally, raise) (2) | 2020.02.07 |
[Python 3.7] 파이썬 다중 상속과 추상 클래스 (3) | 2020.02.07 |
[Python 3.7] 파이썬 클래스 상속 개념과 오버라이딩, super() (3) | 2020.02.07 |