대부분의 프로그래밍 언어와 마찬가지로 Python도 while과 for 문을 통해 반복문 구조를 제공합니다. 특히 for 문은 리스트, 튜플, 문자열 같은 이터러블(iterable) 객체를 순회할 때 매우 유용합니다. 그런데 Python의 표준 라이브러리에는 이보다 더 효율적이고 빠른 반복 도구를 담고 있는 itertools 모듈이 존재합니다. 이 모듈의 반복자 빌딩 블록들은 Haskell, SML 같은 함수형 프로그래밍 언어의 유사 도구들을 Python 스타일로 구현한 것입니다.
itertools 모듈 함수의 세 가지 유형
itertools 모듈의 함수는 다음 세 가지 범주로 나눌 수 있습니다.
- 무한 반복자(Infinite iterators)
- 유한 반복자(Finite iterators)
- 조합형 반복자(Combinatoric iterators)
무한 반복자
다음 함수들은 끝없는 무한 시퀀스를 생성합니다.
count()
시작 값부터 일정한 간격으로 증가하는 값을 갖는 반복자를 반환합니다. 선택적으로 step 인수를 지정하면 그 간격만큼 값이 증가합니다.
>>> from itertools import count >>> for x in count(20): print (x) >>> for x in count(100,10): print (x)
첫 번째 문은 20부터 시작하는 무한 수열을, 두 번째 문은 100부터 10씩 증가하는 수열을 생성합니다. 주의할 점은 이 반복문이 무한히 실행되며 스스로 종료되지 않는다는 것입니다. Ctrl-C를 눌러야 중단됩니다.
cycle()
입력받은 이터러블의 각 요소를 하나씩 반환하면서 동시에 복사본을 저장해 둡니다. 원본 요소가 모두 소진되면 저장해 둔 복사본의 요소를 다시 반환하여 무한 루프를 형성합니다.
>>> from itertools import cycle
>>> for x in cycle("hello"):
print (x)키보드 인터럽트(Ctrl-C)를 발생시키기 전까지 문자열의 문자들이 계속해서 출력됩니다.
repeat()
객체 인수를 계속 반복해서 반환합니다. 두 번째 인수 times를 지정하면 해당 횟수만큼만 반복합니다.
>>> from itertools import repeat
>>> for x in repeat(1):
print (x)
>>> for x in repeat('hello', 10):
print (x)첫 번째 루프는 1을 끝없이 출력하고, 두 번째 루프는 'hello'를 10번 출력합니다.
유한 반복자
다음 범주의 함수들은 가장 짧은 입력 시퀀스가 끝나면 종료되는 유한 반복자를 반환합니다.
accumulate()
두 개의 매개변수를 받습니다. 첫 번째는 리스트, 튜플, 문자열 같은 이터러블이며, 두 번째 매개변수는 기본값으로 operator.add()(표준 덧셈 연산자를 구현한 operator 모듈의 함수)입니다. 숫자 두 개를 받는 다른 함수로 대체할 수도 있습니다.
accumulate(sequence, func)
입력 시퀀스의 처음 두 요소가 func로 처리되고, 그 결과가 다음 반복의 첫 번째 인수가 되며, 두 번째 인수로는 입력 시퀀스의 세 번째 요소가 전달됩니다. 이 과정이 시퀀스가 소진될 때까지 반복되며, accumulate 함수는 연속된 요소들의 누적 처리 결과를 요소로 갖는 반복자를 반환합니다.
아래 예제에서는 리스트의 숫자들이 누적으로 더해집니다. 기본 함수 인수가 덧셈 연산이라는 점에 유의하세요.
>>> from itertools import accumulate >>> list(accumulate([1,2,3,4,5])) [1, 3, 6, 10, 15]
사용자가 직접 정의한 함수를 accumulate()의 두 번째 매개변수로 사용할 수도 있습니다.
>>> def multiply(x,y): return x*y >>> list(accumulate([1,2,3,4,5], multiply)) [1, 2, 6, 24, 120]
이 동작은 내장 함수 reduce()와 다소 비슷하지만 차이점이 있습니다. reduce()는 누적의 최종 결과만 반환하는 반면, accumulate()는 모든 중간 결과를 담은 반복자를 만들어냅니다.
chain()
둘 이상의 이터러블을 매개변수로 받을 수 있습니다. 첫 번째 이터러블의 각 요소를 결과 반복자에 하나씩 산출(yield)하고, 이터러블 매개변수가 모두 소진될 때까지 다음 이터러블로 넘어가며 진행합니다.
>>> from itertools import chain >>> list(chain([10,20],'hello',range(4))) [10, 20, 'h', 'e', 'l', 'l', 'o', 0, 1, 2, 3]
dropwhile()
술어(predicate) 함수 인수가 참(True)을 반환하는 동안 이터러블에서 요소를 건너뛰다가, 함수가 거짓(False)을 반환하는 순간 남은 모든 요소를 반복자에 산출합니다.
>>> from itertools import dropwhile >>> def iseven(x): if x % 2 == 0: return True else: return False >>> list(dropwhile(iseven, [12,90,61,4,15])) [61, 4, 15]
filterfalse()
술어 함수의 결과가 False인 요소들만 걸러내어 반복자로 반환합니다. 즉, 조건을 만족하지 않는 요소만 남기는 필터입니다.
>>> from itertools import filterfalse >>> def iseven(x): if x % 2 == 0: return True else: return False >>> list(filterfalse(iseven, [12,90,61,4,15])) [61, 15]
islice()
이터러블에서 특정 요소들을 선택해 반복자를 만듭니다. 선택 기준은 start, stop, step 매개변수에 따라 결정됩니다. start 값부터 선택을 시작하며 stop 값까지 진행합니다. stop이 None이면 이터러블의 끝까지, 아니라면 지정된 인덱스에서 멈춥니다. step 매개변수는 기본값이 1이며, 요소 선택이 이 간격으로 증가합니다. 어떤 매개변수도 음수일 수 없습니다.
>>> from itertools import islice >>> list(islice(range(10),1,5,2)) [1, 3] >>> list(islice(range(10),0,None,3)) [0, 3, 6, 9] >>> list(islice(range(10),5,None)) [5, 6, 7, 8, 9] >>> list(islice(range(10),5)) [0, 1, 2, 3, 4]
조합형 반복자
다음 함수들은 이터러블 객체로부터 조합론적(combinatoric) 반복자를 생성합니다.
product()
입력 시퀀스들의 요소들에 대한 데카르트 곱(Cartesian product)인 반복자를 생성합니다. 이는 각 이터러블 시퀀스를 순회하는 중첩 for 루프를 구성하는 것과 동일합니다.
다음 예제에서는 리스트 컴프리헨션 기법을 사용해 두 시퀀스 위에서 중첩 루프를 돌며 데카르트 곱을 만듭니다.
>>> [[x,y] for x in [1,2,3] for y in ['a','b','c']] [[1, 'a'], [1, 'b'], [1, 'c'], [2, 'a'], [2, 'b'], [2, 'c'], [3, 'a'], [3, 'b'], [3, 'c']]
product() 함수도 유사한 결과를 만들어냅니다.
>>> from itertools import product >>> list(product([1,2,3],['a','b','c'])) [(1, 'a'), (1, 'b'), (1, 'c'), (2, 'a'), (2, 'b'), (2, 'c'), (3, 'a'), (3, 'b'), (3, 'c')]
permutations()
입력 이터러블의 요소들로 만들 수 있는 모든 순열(permutation)을 산출합니다. 각 순열의 길이는 두 번째 매개변수로 지정할 수 있으며, 지정하지 않으면 길이는 1입니다.
>>> list(permutations(range(1,4),2)) [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]
combinations()
입력 이터러블의 요소들로 만들 수 있는 모든 조합(combination)을 산출합니다. 마찬가지로 각 조합의 길이는 두 번째 매개변수로 지정할 수 있으며, 생략하면 길이는 1입니다.
>>> from itertools import combinations >>> list(combinations(range(1,4),2)) [(1, 2), (1, 3), (2, 3)]
마무리
이 글에서는 Python 표준 라이브러리의 itertools 모듈에 정의된 다양한 반복자 도구들을 살펴보았습니다. count()와 cycle() 같은 무한 반복자부터 accumulate(), chain(), islice() 같은 유한 반복자, 그리고 product(), permutations(), combinations() 같은 조합형 반복자까지 잘 활용하면 반복 코드를 더 간결하고 효율적으로 작성할 수 있습니다.