itertools는 문법 카드가 아니라 반복 흐름을 조합하는 도구 모음입니다.
즉 "중간 리스트를 자꾸 만들지 않고 iterator로 이어 가고 싶은가"가 이 카드의 출발점이고, chain, islice, product, groupby를 각각 어떤 질문에 쓰는지 구분하는 편이 중요합니다.
숏컷 코드
from itertools import chain, islice, product
flat = list(chain([1, 2], [3, 4]))
first_three = list(islice(range(10), 3))
pairs = list(product(["x", "y"], [1, 2]))문법
기본형은 자주 쓰는 도구 import부터 보면 됩니다.
from itertools import chain, islice, product, groupby흐름 조합
여러 iterable을 한 흐름으로 붙이면 chain()
chain()은 여러 iterable을 하나의 긴 흐름처럼 이어 줍니다.
from itertools import chain
merged = chain([1, 2], [3, 4], [5])리스트를 계속 합치는 대신, 기존 iterable 성격을 유지하면서 연결하고 싶을 때 잘 맞습니다.
앞부분만 조금 보면 islice()
큰 iterator에서 일부만 미리 보고 싶거나, 처음 몇 개만 소비하고 싶다면 islice()가 자연스럽습니다.
from itertools import islice
preview = list(islice(range(100), 5))전체를 리스트로 바꾸지 않고 앞부분만 꺼낼 수 있다는 점이 핵심입니다.
가능한 조합을 만들면 product()
카테고리와 옵션, 좌표와 방향처럼 가능한 모든 조합을 만들고 싶을 때 product()를 씁니다.
from itertools import product
pairs = list(product(["A", "B"], [1, 2]))중첩 루프를 명시적으로 적는 대신 "조합 생성"이라는 의도를 직접 드러낼 수 있습니다.
그룹과 파이프라인
연속된 같은 키를 묶으면 groupby()
groupby()는 같은 키 전체를 알아서 모아 주는 도구가 아니라, 이미 정렬된 연속 구간을 묶는 도구입니다.
from itertools import groupby
rows = sorted(rows, key=lambda row: row["team"])
for team, group in groupby(rows, key=lambda row: row["team"]):
print(team, list(group))이 점 때문에 SQL의 GROUP BY처럼 생각하면 자주 헷갈립니다.
iterator 파이프라인이 읽기 어려워지면 리스트나 루프로 돌아간다
itertools는 강력하지만, 연산이 지나치게 길게 이어지면 오히려 흐름을 파악하기 어려울 수 있습니다.
이럴 때는 중간에 리스트로 끊거나, 명시적 루프로 다시 풀어 쓰는 편이 낫습니다.
주의할 점
groupby()는 같은 키 전체를 자동으로 모아 주지 않고, 연속된 구간만 묶습니다. 보통 먼저 정렬이 필요합니다.
# ❌ 정렬 없이 쓰면 같은 키가 여러 그룹으로 나뉠 수 있음
from itertools import groupby
rows = [{"team": "B"}, {"team": "A"}, {"team": "B"}]
for team, group in groupby(rows, key=lambda row: row["team"]):
print(team, list(group))
# ✅ 같은 키 기준으로 먼저 정렬
rows = sorted(rows, key=lambda row: row["team"])
for team, group in groupby(rows, key=lambda row: row["team"]):
print(team, list(group))참고 링크
2 sources