매직 메서드 - 반복
1. 반복 가능한 객체
파이썬에서는 for 루프를 만나면 해당되는 객체의 __iter__를 실행합니다. 그리고 __next__를 순차적으로 실행합니다. 이를 풀면 아래와 같은 코드가 됩니다.
for i in 'hello':
print(i)
my_iterator = iter('hello') # __iter__ 실행, for 루프에서 실행
i = next(my_iterator) # __next__ 실행
print(i)
i = next(my_iterator)
print(i)
i = next(my_iterator)
print(i)
i = next(my_iterator)
print(i)
i = next(my_iterator)
print(i)
i = next(my_iterator)
print(i) # StopIteration 예외 발생, 반복문 탈출
2. __iter__, __next__ 매직 메서드
파이썬에서는 __iter__ 매직 메서드를 사용하여 반복 가능한 객체를 만들 수 있습니다. 이 메서드는 __next__ 메서드를 가진 이터레이터 객체를 반환해야 합니다.
class MyRange:
def __init__(self, start, end):
self.start = start
self.end = end
def __iter__(self):
return self
def __next__(self):
if self.start >= self.end:
raise StopIteration
value = self.start
self.start += 1
return value
my_range = MyRange(0, 3)
for i in my_range:
print(i)
다만 이렇게 순회를 하면 한 번만 순회할 수 있습니다. 이를 해결하기 위해서는 이터레이터를 새로 만들어야 합니다.
3. 이터레이터 재사용
이터레이터를 재사용하고 싶다면 __iter__ 메서드에서 self.current_value 변수를 초기화해야 합니다.
class MyIterator:
def __init__(self, stop):
self.stop = stop
def __iter__(self):
self.current_value = 0 # __iter__에서 초기화
return self
def __next__(self):
if self.current_value >= self.stop:
raise StopIteration
result = self.current_value
self.current_value += 1
return result
my_iterator = MyIterator(5)
for i in my_iterator:
print(i)
for i in my_iterator:
print(i)
💡 위 코드는 https://pythontutor.com/python-debugger.html#mode=edit 서비스를 통해 시각화해서 단계별로 실행해보면서 이해하시길 권해드립니다.
4. 이터러블과 이터레이터 분리
이 두가지 용어를 혼동하기 쉽지만, 이터러블은 __iter__ 메서드를 가진 객체를 말하고, 이터레이터는 __next__ 메서드를 가진 객체를 말합니다.
우리가 흔히 순회가능한 객체라고 부르는 것은 이터러블 객체입니다. 이 두개를 아래처럼 분리할 수도 있습니다.
class NumberIterator:
def __init__(self, start, end):
self.current = start
self.end = end
def __next__(self):
if self.current >= self.end:
raise StopIteration
value = self.current
self.current += 1
return value
class Numbers:
def __init__(self, start, end):
self.start = start
self.end = end
def __iter__(self):
return NumberIterator(self.start, self.end)
nums = Numbers(1, 4)
# 첫 번째 반복
for num in nums:
print(f"첫번째: {num}")
# 두 번째 반복 (새로운 이터레이터가 생성됨)
for num in nums:
print(f"두번째: {num}")
5. yield를 사용한 이터레이터
yield 키워드를 사용하면 이터레이터를 간단하게 만들 수 있습니다.
class EvenNumbers:
def __init__(self, end):
self.end = end
def __iter__(self):
for i in range(0, self.end, 2):
yield i
evens = EvenNumbers(7)
for num in evens:
print(num) # 0, 2, 4, 6