iterator는 반복될 수 있는 객체이다. 모든 값들을 순회할 수 있다는 뜻 이다. 파이썬에서 좀 더 명확히 말하자면, 이터레이터는 __iter__()
와 __next__()
메서드로 구성된 이터레이터 프로토콜을 구현한 객체이다.
리스트, 튜플, 딕셔너리, 세트 모두가 iterable한 객체이다. 이들은 우리가 iterator 객체를 가질 수 있게 해주는 iterable container이다.
iter()
메서드를 사용하여 iterator 객체를 얻을 수 있다.
mytuple = ("apple", "banana", "cherry")
myit = iter(mytuple)
print(next(myit))
print(next(myit))
print(next(myit))
>>> python main.py
apple
banana
cherry
심지어 문자열도 iterable한 객체이기 때문에 iter()
를 사용하면 iterator를 얻을 수 있다.
mystr = "banana"
myit = iter(mystr)
print(next(myit))
print(next(myit))
print(next(myit))
print(next(myit))
print(next(myit))
print(next(myit))
>> python main.py
b
a
n
a
n
a
for
반복문을 사용해 iterable 객체를 루핑할 수 있다.
mytuple = ("apple", "banana", "cherry")
for x in mytuple:
print(x)
>> python main.py
for
반복문은 실제로 iterator 객체를 생성한 후 next()
를 매회마다 실행한다.
이터레이터로서의 클래스를 만들기 위해서 __iter__()
메서드와 __next__()
메서드를 반드시 구현해야한다. 파이썬에서 클래스에 대해 처음 배울 때, 모든 클래스는 객체를 생성할 때 무언가 초기화를 해주는 __init__()
메서드를 포함한다고 공부한다. __iter__()
메서드도 비슷(?)하다. 어떤 동작을 구현하던 마지막에는 반드시 iterator 객체를 반환해야 한다. __next__()
메서드도 마찬가지로 어떤 기능을 구현하던 마지막에는 반드시 시퀀스의 다음 아이템을 반환해야한다.
다음은 1부터 숫자를 하나씩 늘려가며 리턴하는 이터레이터의 예제이다.
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
x = self.a
self.a += 1
return x
myclass = MyNumbers()
myiter = iter(myclass)
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
print(next(myiter))
>> python main.py
1
2
3
4
5
위의 예제는 원한다면 next() 호출을 영원히 계속 할 수 있고 for loop에서 사용되면 무한루프에 빠지게된다. 계속 반복되는 것을 방지하기 위해 StopIteration
을 사용한다.
아래는 a가 20보다 크면 StopIteration을 발생시키는 예제이다.
class MyNumbers:
def __iter__(self):
self.a = 1
return self
def __next__(self):
if self.a <= 20:
x = self.a
self.a += 1
return x
else:
raise StopIteration
myclass = MyNumbers()
myiter = iter(myclass)
for x in myiter:
print(x)
>> python main.py
1
2
... 생략
19
20
다음의 간단한 키를 출력하는 딕셔너리에 대한 for 문을 while문으로 구현해 보세요.
D = {'a':1, 'b':2, 'c':3}
for key in D.keys():
print(key)
StopIteration에 대해 공부하고 났더니 방향이 보였다. 일부러 StopIteration을 발생시켜 while문을 빠져나가는 것으로 생각하여 코드를 작성하였더니 잘 동작하였다.
D = {'a':1, 'b':2, 'c':3}
keys = iter(D.keys())
while True:
try:
key = next(keys)
except StopIteration:
break
print(key)
>> python assignment.py
a
b
c
iterator를 공부하기 전에는 그냥 for문에 동작하도록 만들어진줄 알았다. 그래서 "좀 잘만들었디 🌈" 이렇게 생각하고 있었는데, 지금은 그 실체를 좀 파악한 것 같다. 바름님께서 데코레이터에 관한 질문에 답변을 해주실 때 "당장은 직접 이런 코드를 구현할 일이 없지만, 나중에 장고같은 프레임워크를 커스터마이징해야 하는 경우가 있는데, 그 때 프레임워크의 코드를 수정하려면 당장 쓰지 않아도 파이썬 문법에 익숙해져 있어야 수월하게 다가갈 수 있다"라고 말씀하셨는데, 지금 그 iterator도 비슷한 맥락으로 받아들이면 될 것 같다.