개발언어 Back-End/Python

itertools(반복자 도구) 파이썬 라이브러리 정리

bluebamus 2025. 2. 15.

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() 사용 시 데이터가 정렬되어 있어야 합니다.
   - 메모리 사용량을 모니터링하고 필요한 경우 청크 단위로 처리해야 한다.

 

 

 

댓글