itertools(반복자 도구) 파이썬 라이브러리 정리
1. 개요
- itertools 모듈은 파이썬에서 반복자(iterator)를 효율적으로 생성하고 다루기 위한 여러 도구들을 모아둔 표준 라이브러리이다.
- 이 모듈의 함수들은 메모리 효율이 뛰어나며, 특히 무한 반복자(infinite iterator)나 조합·순열 등 다양한 반복 작업을 간단하게 구현할 수 있도록 도와준다.
2. 주요 특징
- 메모리 효율성: 요소들을 한 번에 하나씩 생성하여 메모리 사용을 최소화
- 고성능: C로 구현되어 빠른 실행 속도 제공
- 조합 가능성: 여러 함수들을 연결하여 복잡한 데이터 처리 파이프라인 구축 가능
- 지연 평가: 실제로 필요할 때까지 계산을 미루는 방식 채택
3. 카테고리별 함수 설명
1) 무한 이터레이터
1. count(start=0, step=1)
- 지정된 시작값(start)부터 시작하여, 매 호출 시마다 step만큼 증가하는 숫자를 무한히 생성하는 반복자(iterator)를 반환한다.
- 무한 반복자: 조건 없이 사용하면 계속해서 값을 생성하기 때문에, 반드시 중단 조건을 두거나 필요한 만큼만 사용해야 한다.
- 다양한 숫자 타입 지원: 정수, 실수, 복소수 등 숫자 타입 모두 지원한다.
- 주요 사용 사례:
- 시퀀스에 인덱스 부여
- 등차수열 생성
- 무한 루프에서 카운터로 활용
from itertools import count
counter = count(10, 2) # 10부터 시작해서 2씩 증가
# 결과: 10, 12, 14, 16, ...
from itertools import count
# 기본: start=0, step=1
for num in count():
if num > 5:
break
print(num, end=" ") # 0 1 2 3 4 5
# start와 step을 지정
for num in count(start=10, step=2):
if num > 20:
break
print(num, end=" ") # 10 12 14 16 18 20
2. cycle(iterable)
- 주어진 이터러블을 무한히 반복한다.
- 주요 사용 사례:
- 순환 패턴 생성
- 라운드 로빈 알고리즘 구현
- 반복되는 작업 스케줄링
from itertools import cycle
colors = cycle(['red', 'green', 'blue'])
# 결과: red, green, blue, red, green, blue, ...
3. repeat(element [, times])
- 요소를 무한히 또는 지정된 횟수만큼 반복한다.
- 주요 사용 사례:
- 고정값 생성
- map()과 함께 사용하여 상수 인자 제공
- 테스트 데이터 생성
from itertools import repeat
repeater = repeat("A", 3)
# 결과: A, A, A
2) 유한 이터레이터
1. accumulate(iterable[, func, *, initial=None])
- 누적 계산 결과를 생성한다.
- 주요 사용 사례:
- 누적 합계 계산
- 이동 평균 계산
- 시계열 데이터 처리
from itertools import accumulate
import operator
# 누적 합계
nums = [1, 2, 3, 4]
list(accumulate(nums)) # [1, 3, 6, 10]
# 누적 곱
list(accumulate(nums, operator.mul)) # [1, 2, 6, 24]
2. chain(*iterables)
- 여러 이터러블을 순차적으로 연결한다.
- 주요 사용 사례:
- 여러 리스트 연결
- 데이터 스트림 통합
- 순차적 처리 파이프라인 구성
from itertools import chain
numbers = chain([1, 2, 3], ['a', 'b', 'c'])
# 결과: 1, 2, 3, a, b, c
3. chain.from_iterable(iterable)
- 중첩 이터러블(예: 리스트의 리스트, 튜플의 튜플 등)을 한 줄로 이어붙여서 단일 이터레이터로 만드는 데 사용한다.
- 동작 방식:
- 전달된 iterable의 각 요소를 순회하며, 그 요소가 또 다른 이터러블이라면 그 내부의 요소들을 차례대로 반환한다.
- chain(*iterable)와 동일한 효과를 내지만, 인자를 언패킹할 필요가 없어서 코드가 더 깔끔해진다.
- 장점:
- 메모리 효율적: 중간에 불필요한 리스트나 튜플을 생성하지 않고, 제너레이터 방식으로 요소들을 순차적으로 반환한다.
- 가독성: 중첩된 이터러블을 평탄화(flatten)할 때 직관적으로 사용할 수 있다.
from itertools import chain
nested_list = [[1, 2, 3], [4, 5], [6]]
flat_iter = chain.from_iterable(nested_list)
# 결과 확인
print(list(flat_iter)) # 출력: [1, 2, 3, 4, 5, 6]
4. compress(data, selectors)
- 설명:
- selectors의 값이 참(True)인 위치의 data 요소만 선택하여 반환한다.
- 주요 파라미터:
- data: 필터링 대상 iterable
- selectors: 각 요소에 대해 참/거짓 값을 가진 iterable
from itertools import compress
data = ['A', 'B', 'C', 'D']
selectors = [True, False, True, False]
result = list(compress(data, selectors))
print(result) # 출력: ['A', 'C']
5. dropwhile(predicate, iterable)
- 설명:
- iterable의 처음부터 predicate가 참인 동안 요소를 건너뛰고, 처음 거짓이 되는 순간부터 모든 요소를 반환한다.
- 주요 파라미터:
- predicate: 함수, 각 요소에 대해 True/False 판단
- iterable: 입력 시퀀스
from itertools import dropwhile
data = [1, 3, 5, 2, 4, 6]
# 4보다 작은 동안은 건너뛰고, 처음 5가 나오면 그 이후 모든 값을 반환
result = list(dropwhile(lambda x: x < 4, data))
print(result) # 출력: [5, 2, 4, 6]
6. filterfalse(predicate, iterable)
- 설명:
- predicate 함수가 거짓(False)을 반환하는 요소들만 걸러내어 반환한다.
- 주요 파라미터:
- predicate: 조건 함수
- iterable: 입력 시퀀스
from itertools import filterfalse
data = [0, 1, 2, 3, 4, 5]
# 짝수에 대해 (x % 2 == 0) True가 되므로 걸러내고 홀수만 반환
result = list(filterfalse(lambda x: x % 2 == 0, data))
print(result) # 출력: [1, 3, 5]
7. groupby(iterable, key=None)
- 연속된 동일한 key 값을 가지는 요소들을 그룹화하여 (key, group) 튜플을 반환한다. (입력 데이터가 미리 정렬되어 있어야 올바르게 그룹화된다.)
- 주요 파라미터:
- iterable: 입력 시퀀스
- key: 각 요소에 적용할 함수 (기본: 항등함수)
- 주요 사용 사례:
- 데이터 분류
- 로그 파일 분석
- 연속된 중복 요소 처리
from itertools import groupby
from operator import itemgetter
data = [("A", 1), ("A", 2), ("B", 1), ("B", 2)]
for key, group in groupby(data, itemgetter(0)):
print(key, list(group))
# 결과:
# A [(A, 1), (A, 2)]
# B [(B, 1), (B, 2)]
from itertools import groupby
data = "AAABBBCCDDAA"
# groupby는 연속된 동일한 값끼리 그룹화합니다.
for key, group in groupby(data):
print(key, list(group))
# 출력:
# A ['A', 'A', 'A']
# B ['B', 'B', 'B']
# C ['C', 'C']
# D ['D', 'D']
# A ['A', 'A']
8. islice(iterable, start, stop[, step])
- 설명:
- iterable의 슬라이스를 생성한다. 리스트 슬라이싱과 유사하지만 메모리 효율적으로 iterator 형태로 반환된다.
- 주요 파라미터:
- iterable: 입력 시퀀스
- start: 시작 인덱스 (기본값: 0)
- stop: 종료 인덱스 (stop 미포함)
- step: 간격 (기본값: 1)
from itertools import islice
data = range(10) # 0부터 9까지
# 인덱스 2부터 8까지 2씩 건너뛰며 요소 추출
result = list(islice(data, 2, 8, 2))
print(result) # 출력: [2, 4, 6]
# 좋은 예
from itertools import islice
# 처음 1000개 요소만 처리
for x in islice(count(), 1000):
process(x)
# 피해야 할 예
# 모든 결과를 메모리에 저장
result = list(combinations(range(1000), 2))
9. starmap(function, iterable)
- 설명:
- 각 요소가 튜플(또는 iterable)인 iterable에 대해, 해당 튜플의 값을 풀어서 function의 인자로 전달하며 결과를 반환한다.
- 주요 파라미터:
- function: 적용할 함수
- iterable: 각 요소가 인자들의 묶음인 iterable
from itertools import starmap
data = [(2, 3), (3, 2), (4, 0.5)]
# pow(x, y)를 각 튜플에 적용: 2**3, 3**2, 4**0.5
result = list(starmap(pow, data))
print(result) # 출력: [8, 9, 2.0]
10. takewhile(predicate, iterable)
- 설명:
- iterable의 처음부터 predicate가 참인 동안의 요소들을 반환한다.
- 주요 파라미터:
- predicate: 조건 함수
- iterable: 입력 시퀀스
from itertools import takewhile
data = [1, 3, 5, 2, 4, 6]
# 5보다 작은 동안의 요소들만 추출
result = list(takewhile(lambda x: x < 5, data))
print(result) # 출력: [1, 3]
11. tee(iterable, n=2)
- 설명:
- 하나의 이터러블(iterable)에서 여러 개의 독립적인 이터레이터(iterator)를 생성할 때 사용된다.
- 주요 파라미터:
- iterable: 입력 시퀀스
- n: 생성할 이터레이터 개수 (기본값: 2)
from itertools import tee
data = [1, 2, 3, 4]
it1, it2 = tee(data, n=2)
print(list(it1)) # 출력: [1, 2, 3, 4]
print(list(it2)) # 출력: [1, 2, 3, 4]
import itertools
data = [1, 2, 3, 4]
iter1, iter2 = itertools.tee(data, 2)
12. zip_longest(iterables, fillvalue=None)
- 설명:
- 여러 iterable을 병렬로 묶되, 가장 긴 iterable의 길이에 맞춰 부족한 값은 fillvalue로 채워 반환한다.
- 주요 파라미터:
- *iterables: 묶을 여러 iterable
- fillvalue: 부족한 부분을 채울 값 (기본값: None)
from itertools import zip_longest
list1 = [1, 2, 3]
list2 = ['a', 'b']
result = list(zip_longest(list1, list2, fillvalue='-'))
print(result) # 출력: [(1, 'a'), (2, 'b'), (3, '-')]
3) 조합형 이터레이터
1. combinations(iterable, r)
- 주어진 iterable의 요소들로부터 순서를 고려하지 않는 r개 조합을 생성한다.
- 주요 파라미터:
- iterable: 조합을 구성할 요소
- r: 선택할 요소의 개수
- 주요 사용 사례:
- 가능한 조합 탐색
- 테스트 케이스 생성
- 알고리즘 문제 해결
from itertools import combinations
list(combinations('ABC', 2))
# 결과: [('A', 'B'), ('A', 'C'), ('B', 'C')]
from itertools import combinations
data = ['A', 'B', 'C']
result = list(combinations(data, 2))
print(result)
# 출력: [('A', 'B'), ('A', 'C'), ('B', 'C')]
2. permutations(iterable, r=None)
- 주어진 iterable의 요소들로부터 길이 r의 모든 순열(순서가 고려된 배열)을 생성한다.
- r이 지정되지 않으면 iterable의 전체 길이의 순열을 생성한다.
- 주요 파라미터:
- iterable: 순열을 구성할 요소
- r: 각 순열의 길이 (기본값: len(iterable))
- 주요 사용 사례:
- 가능한 순서 탐색
- 경로 탐색 문제
- 암호 생성
from itertools import permutations
list(permutations('ABC', 2))
# 결과: [('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
from itertools import permutations
data = [1, 2, 3]
# 전체 길이의 순열 (r 생략)
result = list(permutations(data))
print(result)
# 출력: [(1, 2, 3), (1, 3, 2), (2, 1, 3),
# (2, 3, 1), (3, 1, 2), (3, 2, 1)]
# 길이가 2인 순열
result2 = list(permutations(data, 2))
print(result2)
# 출력: [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]
3. product(*iterables, repeat=1)
- 주어진 여러 iterable의 데카르트 곱(모든 가능한 조합)을 생성한다.
- repeat 인자를 사용하면 동일 iterable의 반복 곱도 구할 수 있다.
- 주요 파라미터:
- iterables: 데카르트 곱을 구할 대상 여러 iterable
- repeat: 각 iterable을 몇 번 반복할 것인지 (기본값: 1)
- 주요 사용 사례:
- 모든 가능한 조합 생성
- 설정 옵션 조합
- 격자 좌표 생성
from itertools import product
list(product('AB', '12'))
# 결과: [('A', '1'), ('A', '2'), ('B', '1'), ('B', '2')]
from itertools import product
# 두 개의 리스트에 대해 데카르트 곱
result = list(product(['A', 'B'], ['1', '2']))
print(result)
# 출력: [('A', '1'), ('A', '2'), ('B', '1'), ('B', '2')]
# 동일 iterable을 3번 반복하는 경우
result_repeat = list(product('AB', repeat=3))
print(result_repeat)
# 출력: [('A', 'A', 'A'), ('A', 'A', 'B'), ('A', 'B', 'A'),
# ('A', 'B', 'B'), ('B', 'A', 'A'), ('B', 'A', 'B'),
# ('B', 'B', 'A'), ('B', 'B', 'B')]
4. combinations_with_replacement(iterable, r)
- 설명:
- 주어진 iterable의 요소들로부터 동일한 요소를 중복 선택할 수 있는 조합(순서를 고려하지 않음)을 생성한다.
- 주요 파라미터:
- iterable: 조합을 구성할 요소
- r: 선택할 요소의 개수
from itertools import combinations_with_replacement
data = ['A', 'B', 'C']
result = list(combinations_with_replacement(data, 2))
print(result)
# 출력: [('A', 'A'), ('A', 'B'), ('A', 'C'),
# ('B', 'B'), ('B', 'C'), ('C', 'C')]
4. 성능 고려사항
1) 메모리 사용
- 이터레이터는 모든 결과를 동시에 메모리에 저장하지 않는다.
- 대용량 데이터 처리에 적합하다.
- list()로 변환 시 모든 결과가 메모리에 로드되므로 주의가 필요하다.
2) 실행 속도
- C로 구현되어 있어 순수 파이썬 구현보다 빠르다.
- 지연 평가로 인해 필요한 만큼만 계산한다.
- 무한 이터레이터 사용 시 종료 조건 설정이 중요하다.
5. 모범 사례
1) 체이닝 패턴
from itertools import chain, filterfalse, takewhile
# 여러 함수를 조합하여 복잡한 데이터 처리
result = takewhile(lambda x: x < 100,
filterfalse(lambda x: x % 2,
chain.from_iterable(range(10) for _ in range(10))))
1. chain.from_iterable(range(10) for _ in range(10))
- 내부 제너레이터 표현식:
- range(10) for _ in range(10)
- 10번 반복하며, 매 반복마다 range(10) (즉, 0부터 9까지의 숫자)를 생성한다.
- chain.from_iterable:
- 여러 개의 iterable (여기서는 10개의 range(10))를 하나의 연속된 이터레이터로 “평탄화”한다.
- 결과적으로, 0부터 9까지의 숫자가 10번 반복된 100개의 숫자를 순차적으로 생성한다.
2. filterfalse(lambda x: x % 2, ...)
- lambda x: x % 2:
- 각 숫자 x에 대해 x % 2의 결과를 평가한다.
- 홀수인 경우에는 x % 2가 1 (참)이고, 짝수인 경우에는 0 (거짓)이 된다.
- filterfalse:
- 주어진 함수의 결과가 **거짓(False)**인 요소들만 통과시킨다.
- 즉, 짝수만 남기게 된다.
- 결과:
- 앞서 평탄화된 100개의 숫자 중 짝수(0, 2, 4, 6, 8 등)만 필터링된다.
3. takewhile(lambda x: x < 100, ...)
- lambda x: x < 100:
- 각 숫자가 100보다 작은지 확인한다.
- takewhile:
- 이 조건이 참인 동안만 숫자를 반환하고, 처음 조건에 맞지 않는 숫자를 만나면 그 시점에서 더 이상 숫자를 처리하지 않는다.
- 특이사항:
- 이 경우, 모든 숫자(0, 2, 4, ... 등)가 100 미만이기 때문에 조건이 항상 참이다.
- 따라서, 필터링된 모든 짝수들이 그대로 반환된다.
- 최종 결과
- result는 0, 2, 4, 6, 8, 0, 2, 4, 6, 8, ... 와 같이 짝수만 순차적으로 포함하는 이터레이터이다.
- 각 함수가 **지연 평가(lazy evaluation)**를 사용하기 때문에, 메모리에 전체 리스트를 저장하지 않고 필요할 때마다 값을 생성하여 효율적으로 처리할 수 있다.
6. 주의사항
- 무한 이터레이터 사용 시 반드시 종료 조건을 설정해야 한다.
- 대용량 데이터 처리 시 list() 변환을 피해야 한다.
- 이터레이터는 한 번만 소비할 수 있으므로, 필요한 경우 tee()를 사용하여 복제해야 한다.
- groupby() 사용 시 데이터가 정렬되어 있어야 합니다.
- 메모리 사용량을 모니터링하고 필요한 경우 청크 단위로 처리해야 한다.
'개발언어 Back-End > Python' 카테고리의 다른 글
Python Logging 설정 파일 매뉴얼 (0) | 2025.02.13 |
---|---|
Python Logging 사용 방법 정리 (0) | 2025.02.13 |
컴프리헨션 / 제너레이터 정리 / 지연평가 (0) | 2025.02.12 |
파이썬 기반 docker 라이브러리 정리 (0) | 2025.02.09 |
파이썬에서 CPU와 메모리 사용량을 추적하는 방법 (0) | 2024.05.14 |
댓글