값을 차례대로 꺼낼 수 있는 객체들을 이터레이터라고 한다.
range는 숫자를 모두 만들어 내는것이 아니라 숫자를 차례대로 꺼낼 수 있는 이터레이터를 만들어 내는 것이다. 반복할 때마다 이터레이터에서 숫자를 하나씩 꺼내서 반복문을 수행하는것 이라고 볼 수 있다.
숫자를 미리 만들게되면 메모리를 너무 많이 사용하기 때문에 성능에 지장을 줄 수 있다.
따라서 이터레이터만 생성하고 값이 필요한 시점에 값을 만드는 방식을 사용한다.
데이터의 생성을 뒤로 미루는 방식을 지역 평가(lazy evaluation)라고 한다.
참고로 이터레이터는 반복자 라고 부르기도 한다.
반복 가능 객체
반복 가능 객체는 요소가 여러개 들어있고 한번에 하나씩 꺼낼 수 있는 객체를 말한다. 반복 가능 객체에는 문자열, 리스트, 딕셔너리, 세트 등이 있다.
객체가 반복가능한 객체인지 알아보는 방법
객체에 __iter__() 메서드가 들어있는지 확인한 후 __iter__() 메서드가 있다면 그건 반복가능한 객체이다.
객체의 메서드를 확인하고 싶다면 dir(객체)를 통해 객체의 메서드를 확인할 수 있다.
dir([1, 2, 3])
# 결과값
# ['__add__',
# '__class__',
# '__class_getitem__',
# '__contains__',
# '__delattr__',
# '__delitem__',
# '__dir__',
# '__doc__',
# '__eq__',
# '__format__',
# '__ge__',
# '__getattribute__',
# '__getitem__',
# '__gt__',
# '__hash__',
# '__iadd__',
# '__imul__',
# '__init__',
# '__init_subclass__',
# '__iter__',
# '__le__',
# '__len__',
# '__lt__',
# '__mul__',
# '__ne__',
# '__new__',
# '__reduce__',
# '__reduce_ex__',
# '__repr__',
# '__reversed__',
# '__rmul__',
# '__setattr__',
# '__setitem__',
# '__sizeof__',
# '__str__',
# '__subclasshook__',
# 'append',
# 'clear',
# 'copy',
# 'count',
# 'extend',
# 'index',
# 'insert',
# 'pop',
# 'remove',
# 'reverse',
# 'sort']
[1, 2, 3].__iter__()
# 결과값
# <list_iterator at 0x7fba9916c040>
it = [1, 2, 3]. __iter__()
it.__next__()
# 1
it.__next__()
# 2
it.__next__()
# 3
it.__next__()
# StopIteration
이터레이터는 __next__() 메서드로 요소를 꺼내다가 꺼낼 요소가 없으면 StopIteration 오류를 발생시켜 반복을 끝낸다.
리스트 뿐만 아니라 문자열, 딕셔너리, 세트도 __iter__() 메서드를 호출하면 이터레이터가 나온다.
"Hello, World!".__iter__()
# 결과값
# <str_iterator at 0x7fafe058f760>
{"a" : 1, "b" : 2}.__iter__()
# 결과값
# <dict_keyiterator at 0x7fb01043fd80>
{1, 2, 3}.__iter__()
# 결과값
# <set_iterator at 0x7fb0000f9c40>
it = range(3).__iter__()
it.__next__()
# 0
it.__next__()
# 1
it.__next__()
# 2
reversed() 메서드
반복 가능한 객체를 뒤집어서 이터레이터로 만들어주는 메서드이다.
reversed([1, 2, 3])
# 결과값
# <list_reverseiterator at 0x7fafe05621a0>
# reversed 함수와 이터레이터
numbers = [1, 2, 3]
r_num = reversed(numbers)
print(r_num)
print(next(r_num))
print(next(r_num))
print(next(r_num))
# 결과값
# <list_reverseiterator object at 0x7fafe0501630>
# 3
# 2
# 1
이터레이터 만들기
class Counter:
def __init__(self, stop):
self.current = 0 # 현재 숫자, 0부터 지정된 숫자 직전까지 반복
self.stop = stop # 반복을 끝낼 숫자
def __iter__(self):
return self # 현재 인스턴스를 반환
def __next__(self):
if self.current < self.stop: # 현재 숫자가 반복을 끝낼 숫자보다 작을 때
r = self.current # 반환할 숫자를 변수에 따로 저장
self.current += 1 # 현재 숫자를 1 증가시킴
return r # 숫자를 반환
else: # 현재 숫자가 반복을 끝낼 숫자보다 크거나 같을 때 예외 발생
raise StopIteration
for i in Counter(3):
print(i)
# 결과값
# 0
# 1
# 2
이터레이터 언패킹
이터레이터의 결과를 변수 여러개에 할당할 수 있다.
이터레이터가 반복하는 횟수와 변수의 갯수가 같아야 가능하다.
a, b, c = Counter(3)
print(a, b, c)
# 결과값
# 0 1 2
"0 1 2".split()
# 결과값
# ['0', '1', '2']
map(int, "0 1 2".split())
# 결과값
# <map at 0x7fafd0fbf940>
a, b, c = map(int, "0 1 2".split())
print(a, b, c)
# 결과값
# 0 1 2
인덱스로 접근할 수 있는 이터레이터
__getitem__ 메서드를 통해서 인덱스로 접근할 수 있는 이터레이터를 생성한다.
print(range(3)[0], range(3)[1], range(3)[2])
# 결과값
# 0 1 2
class Counter:
def __init__(self, stop):
self.current = 0
self.stop = stop
def __getitem__(self, index): # 인덱스를 전달받음
if index < self.stop: # 인데스가 반복을 끝낼 숫자보다 작을때
return index # 인덱스를 반환
else: # 인덱스가 반복을 끝낼 숫자보다 크거나 같을 때
raise IndexError # 예외 발생
print(Counter(3)[0], Counter(3)[1], Counter(3)[2])
# 결과값
# 0 1 2
iter(), next() 함수 활용하기
- iter() : __iter__() 메서드 호출
- next() : __next__() 메서드 호출
it = iter(range(3))
next(it)
# 결과값
# 0
next(it)
# 결과값
# 1
next(it)
# 결과값
# 2
iter
반복을 끝낼 값을 지정하면 특정 값이 나올 때 반복을 끝내는 함수이다.
반복을 끝낼 값을 sentinel(감시병) 이라고 한다.
호출가능한 객체를 사용해야 한다.
iter 표현법
iter(호출 가능한 객체, 반복을 끝낼 값)
improt random
it = iter(lambda : random.randint(0, 5), 2)
next(it)
next(it)
# 4
next(it)
# 0
next(it)
# 5
next(it)
# 3
next(it)
# 4
next(it)
# 3
next(it)
# 4
next(it)
# 5
next(it)
# StopIteration:
# 2가 나오면 StopIteation이 발생한다
for i in iter(lambda : random.randint(0, 5), 2):
print(i, end=" ")
# 결과값
# 3 0 3 4 3 4 1 0 1 5 3
# 위의 코드와 같은 내용
while True:
i = random.randInt(0, 5)
if i == 2:
break
print(i, end = " ")
# 결과값
# 3 3 4 3 0 3 1 4 4 4 4 0 0 3 3 4 1 4 0 1
next
기본값을 지정할 수 있다. 만약 기본값을 지정하면 반복이 끝나더라도 StopIteration 오류가 발생하지 않고 기본값을 출력하게 된다.
반복할 수 있을 때는 해당 값을 출력하고, 반복이 끝나면 기본값을 출력한다.
표현법
next(반복 가능한 객체, 기본값)
it = iter(range(3))
next(it, 10)
# 0
next(it, 10)
# 1
next(it, 10)
# 2
next(it, 10)
# 10
코드 실행 결과를 보면 0, 1, 2 까지 나온뒤에도 에러가 발생하지 않고 계속 10이 출력되는것을 볼 수 있다.