itertools — 효율적인 순회를 위한 반복자 함수

itertools — 효율적인 순회를 위한 반복자 함수

itertools 모듈은 반복자에 대한 빠르고 메모리 효율적인 도구를 제공해요. 여러 가지 "반복자 빌딩 블록"들이 있어서, 순수 Python 도구만으로 만든 것보다 더 간결하고 효율적으로, 그리고 메모리를 아끼면서 조합함수(combination)를 만들 수 있어요.

이 모듈의 함수들은 C로 구현돼 있지만, 그 동작은 순수 Python 코드로도 정확히 재현할 수 있습니다. 여기서는 각 함수의 사용법과 특징을 설명할게요.

출처: Python 표준 라이브러리

무한 반복자 (Infinite iterators)

  • itertools.count(start=0, step=1)start에서 시작해 step씩 증가하는 값을 무한히 만들어 내요. 인덱스나 타임스탬프에 유용합니다.
>>> from itertools import count
>>> for i in count(10, 2):
...     if i > 16: break
...     print(i)
10
12
14
16
  • itertools.cycle(iterable) — 이터러블의 요소를 무한히 반복해요. 절전 상태를 저장해 첫 요소에 대한 포인터를 유지합니다.
>>> from itertools import cycle
>>> list(islice(cycle('AB'), 5))
['A', 'B', 'A', 'B', 'A']
  • itertools.repeat(object, times=None)object를 무한히, 또는 지정된 times 횟수만큼 반복해요. repeatzip을 함께 쓰면, 제네레이터에 곱셈된 상수로 마치 리스트의 각 항목에 같은 값을 곱하는 듯한 효과를 줍니다.

최단 입력을 기준으로 종료되는 반복자 (Iterators terminating on the shortest input sequence)

  • itertools.accumulate(iterable, func=operator.add, *, initial=None) — 누적 합계(또는 이항 함수의 누적 결과)를 만들어 내요.
>>> from itertools import accumulate
>>> list(accumulate([1, 2, 3, 4, 5]))
[1, 3, 6, 10, 15]
  • itertools.batched(iterable, n) — 이터러블을 n개 크기의 튜플로 묶어 만들어 내요. 마지막 배치는 더 짧을 수 있어요.
>>> list(batched('ABCDEFG', 3))
[('A', 'B', 'C'), ('D', 'E', 'F'), ('G',)]
  • itertools.chain(*iterables) — 여러 이터러블을 순서대로 이어 붙여요.
>>> list(chain('ABC', 'DEF'))
['A', 'B', 'C', 'D', 'E', 'F']
  • itertools.chain.from_iterable(iterable) — 이터러블들을 이어 붙이는 생성자. chain(*iter)과 동일하지만 인자가 아닌 이터러블 하나를 받아요.

  • itertools.compress(data, selectors)selectors가 참인 위치의 data 요소만 걸러 내요.

>>> list(compress('ABCDEF', [1, 0, 1, 0, 1, 1]))
['A', 'C', 'E', 'F']
  • itertools.dropwhile(predicate, iterable)predicate가 참인 동안 요소를 버리고, 처음 거짓이 되는 지점부터 나머지를 만들어 내요.
>>> list(dropwhile(lambda x: x < 5, [1, 4, 6, 3, 8]))
[6, 3, 8]
  • itertools.filterfalse(predicate, iterable)predicate가 거짓인 요소만 만들어 내요 (filter의 반대).

  • itertools.groupby(iterable, key=None) — 이터러블을 키 함수의 결과로 그룹화한 (key, group) 쌍을 만들어 내요. 주의: 그룹을 만들려면 입력이 키에 따라 이미 정렬돼 있어야 해요.

>>> [k for k, g in groupby('AAAABBBCCDAABBB')]
['A', 'B', 'C', 'D', 'A', 'B']
  • itertools.islice(iterable, stop) / itertools.islice(iterable, start, stop, step) — 이터러블의 슬라이스를 만들어 내요. 양의 인덱스만 지원합니다.
>>> list(islice('ABCDEFG', 2, 6, 2))
['C', 'E']
  • itertools.pairwise(iterable) — 연속된 겹치는 쌍을 만들어 내요.
>>> list(pairwise('ABCDEF'))
[('A', 'B'), ('B', 'C'), ('C', 'D'), ('D', 'E'), ('E', 'F')]
  • itertools.starmap(function, iterable)function(*item)처럼 이터러블의 각 항목을 풀어서 함수를 호출한 결과를 만들어 내요.

  • itertools.takewhile(predicate, iterable)predicate가 참인 동안 요소를 만들어 내고, 처음 거짓이 되면 멈춰요.

  • itertools.tee(iterable, n=2) — 하나의 이터러블에서 n개의 독립적인 반복자 복사본을 만들어 내요.

  • itertools.zip_longest(*iterables, fillvalue=None) — 여러 이터러블을 가장 긴 것에 맞춰서 묶어요. 짧은 쪽은 fillvalue로 채워요 (내장 zip은 짧은 쪽에 맞춰서 멈춰요).

조합 생성자 (Combinatoric iterators)

  • itertools.product(*iterables, repeat=1) — 카테시안 곱(데카르트 곱)을 만들어 내요.
>>> list(product('AB', repeat=2))
[('A', 'A'), ('A', 'B'), ('B', 'A'), ('B', 'B')]
  • itertools.permutations(iterable, r=None) — 요소들을 길이 r로 연속 나열한 순열을 만들어 내요.
>>> list(permutations('ABC', 2))
[('A', 'B'), ('A', 'C'), ('B', 'A'), ('B', 'C'), ('C', 'A'), ('C', 'B')]
  • itertools.combinations(iterable, r) — 요소들을 길이 r로 조합(순서 무시)해 만들어 내요.
>>> list(combinations('ABC', 2))
[('A', 'B'), ('A', 'C'), ('B', 'C')]
  • itertools.combinations_with_replacement(iterable, r) — 요소 중복을 허용한 조합을 만들어 내요.
>>> list(combinations_with_replacement('ABC', 2))
[('A', 'A'), ('A', 'B'), ('A', 'C'), ('B', 'B'), ('B', 'C'), ('C', 'C')]

순서 규칙을 표로 요약하면:

조합 연산 요소가 순서 있는지 요소 반복 여부
product()
permutations() 아니오
combinations(r) 아니오 아니오
combinations_with_replacement(r) 아니오

레시피 (Recipes)

itertools 문서는 이 함수들을 조합해 파워풀한 도구를 만드는 레시피를 포함해요. 예를 들어:

  • iter_index(iterable, value, start=0, stop=None) — 값이 나타나는 인덱스들을 순회.
  • sieve(n) — n까지의 소수를 찾는 에라토스테네스의 체.
  • batched 기반의 grouper(iterable, n, *, incomplete='fill', fillvalue=None) — 요소들을 고정 크기 그룹으로 묶기.
  • powerset(iterable) — 모든 부분집합(멱집합)을 만드는 레시피.

대표적인 powerset 레시피:

from itertools import chain, combinations

def powerset(iterable):
    "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1))

더 알아보기