파이썬 - 이터레이터

안녕하세요, 파이썬 프로그래머를 꿈꾸는 여러분! 오늘, 우리는 파이썬 이터레이터의 흥미로운 세계로 여행을 떠날 거예요. 여러분의 친절한 이웃 컴퓨터 과학 교사로서, 이 재미있는 주제를 안내해 드리게 되어 기쁩니다. 그럼, 좋아하는 음료를 들고 편하게 앉아서, 같이 들어가 볼까요?

Python - Iterators

파이썬 이터레이터

이터레이터란 무엇인가요?

색종이 레고 블록이 가득한 큰 상자를 생각해 봅시다. 이터레이터는 마법의 손처럼 상자에 손을 내밀어 한 개의 레고 블록씩 꺼내는 것처럼 동작합니다. 이를 통해 상자의 모든 내용물을 바닥에 던지지 않고도 각 블록을 개별적으로 검토할 수 있습니다. 파이썬에서는 이터레이터가 비슷하게 동작하여, 데이터 컬렉션을 하나씩 처리할 수 있게 해줍니다.

이터레이터는 어떻게 동작합니까?

파이썬의 이터레이터는 두 가지 특별한 메서드를 구현하는 객체입니다: __iter__()__next__()입니다. 이것들이 이해하기 어렵게 들릴지 모르지만, 걱정 마세요 - 점점 설명해 나갈게요!

  1. __iter__() 메서드는 이터레이터 객체 자체를 반환합니다. "여, 나는 레고 블록을 주기 시작할 준비가 되었어!"라고 말하는 것과 같아요.
  2. __next__() 메서드는 시퀀스의 다음 항목을 반환합니다. 상자에 손을 내밀어 다음 레고 블록을 꺼내는 것과 같아요.

이제 간단한 예제를 통해 실제로 보겠습니다:

# 목록을 생성합니다 (우리의 레고 블록 상자)
my_list = [1, 2, 3, 4, 5]

# 목록에서 이터레이터를 가져옵니다
my_iterator = iter(my_list)

# next()를 사용하여 하나씩 아이템을 가져옵니다
print(next(my_iterator))  # 출력: 1
print(next(my_iterator))  # 출력: 2
print(next(my_iterator))  # 출력: 3

이 예제에서, iter(my_list)는 우리의 목록에 대한 이터레이터 객체를 생성합니다. 그런 다음, next(my_iterator)를 호출하여 목록에서 다음 항목을 가져옵니다.

루프에서 이터레이터의 힘

여기 재미있는 사실 하나: 파이썬에서 for 루프를 사용하면, 실제로는 배후에서 이터레이터를 사용하고 있습니다! 그렇게 어떻게 동작하는지 보겠습니다:

my_list = ["apple", "banana", "cherry"]

for fruit in my_list:
print(f"I love {fruit}!")

# 출력:
# I love apple!
# I love banana!
# I love cherry!

파이썬은 자동으로 my_list에서 이터레이터를 만들고 __next__()를 사용하여 루프의 각 항목을 가져옵니다. 멋지지 않나요?

이터레이터의 에러 처리

이제, 우리의 마법의 레고 블록 꺼내는 손이 빈 상자에 들어가는 일이 발생하면 어떻게 될까요? 파이썬에서는 이터레이터에 더 이상 항목이 남지 않았을 때 어떤 일이 일어나는지에 대해 설명해 보겠습니다. 이는 에러 처리가 중요한 부분입니다.

이터레이터가 소모되면 (더 이상 항목이 남지 않았을 때), StopIteration 예외를 발생시킵니다. 이를 통해 액션을 볼 수 있습니다:

my_list = [1, 2, 3]
my_iterator = iter(my_list)

print(next(my_iterator))  # 출력: 1
print(next(my_iterator))  # 출력: 2
print(next(my_iterator))  # 출력: 3
print(next(my_iterator))  # StopIteration 예외 발생

이를 부드럽게 처리하기 위해 try-except 블록을 사용할 수 있습니다:

my_list = [1, 2, 3]
my_iterator = iter(my_list)

try:
while True:
item = next(my_iterator)
print(item)
except StopIteration:
print("이터레이터의 끝에 도달했습니다!")

# 출력:
# 1
# 2
# 3
# 이터레이터의 끝에 도달했습니다!

이렇게 하면 모든 항목을 처리하고 이터레이터의 끝을 부드럽게 처리할 수 있습니다.

사용자 정의 이터레이터

이제 이터레이터가 어떻게 동작하는지 이해했으므로, 우리 자신의 이터레이터를 만들어 보겠습니다! 예를 들어, 카운트다운 이터레이터를 만들고 싶다면, 이렇게 할 수 있습니다:

class Countdown:
def __init__(self, start):
self.start = start

def __iter__(self):
return self

def __next__(self):
if self.start <= 0:
raise StopIteration
self.start -= 1
return self.start + 1

# 우리의 사용자 정의 이터레이터를 사용합니다
countdown = Countdown(5)
for number in countdown:
print(number)

# 출력:
# 5
# 4
# 3
# 2
# 1

이 예제에서, Countdown 클래스는 이터러블(=__iter__() 메서드를 가짐)과 이터레이터(=__next__() 메서드를 가짐) 두 가지 역할을 합니다. 각 __next__()가 호출될 때마다 카운트다운 시퀀스의 다음 숫자를 반환합니다.

비동기 이터레이터

더 고급한 주제로 나아가며, 비동기 이터레이터에 대해 간단히 소개해 보겠습니다. 이는 비동기 프로그래밍에서 사용되며, 동시성 코드를 작성하는 방법입니다.

비동기 이터레이터는 정상 이터레이터와 비슷하지만, asyncawait 키워드를 사용합니다. 간단한 예제를 보겠습니다:

import asyncio

class AsyncCountdown:
def __init__(self, start):
self.start = start

def __aiter__(self):
return self

async def __anext__(self):
await asyncio.sleep(1)  # 비동기 연산을 시뮬레이션합니다
if self.start <= 0:
raise StopAsyncIteration
self.start -= 1
return self.start + 1

async def main():
async for number in AsyncCountdown(5):
print(number)

asyncio.run(main())

# 출력 (1초 지연 포함):
# 5
# 4
# 3
# 2
# 1

이 비동기 이터레이터는 우리의 이전 Countdown 클래스와 유사하게 동작하지만, 비동기 연산을 허용합니다 (여기서는 asyncio.sleep(1)로 시뮬레이션).

이터레이터 메서드 표

다음은 우리가 논의한 주요 메서드를 요약한 편리한 표입니다:

메서드 설명 사용될 곳
__iter__() 이터레이터 객체를 반환합니다 정상 이터레이터
__next__() 시퀀스의 다음 항목을 반환합니다 정상 이터레이터
__aiter__() 비동기 이터레이터 객체를 반환합니다 비동기 이터레이터
__anext__() 비동기 시퀀스의 다음 항목을 반환합니다 비동기 이터레이터

그리고 이렇게 끝납니다, 여러분! 파이썬 이터레이터의 세계를 여행하며, 기본부터 우리 자신의 이터레이터를 만들고, 비동기 이터레이터까지 다루었습니다. 레고를 배우는 것처럼, 이터레이터를 마스터하려면 연습이 필요합니다. 그래도 자신의 이터레이터를 실험해 보고 만들어 보세요. 코딩 잘하고, 이터레이터가 여러분의 편에 들어가길 바랍니다!

Credits: Image by storyset