itertools
내장 모듈에는 이터레이터를 조직화하거나 사용할 때 유용한 여러 함수가 들어 있다.
여러 이터레이터 연결하기
chain
여러 이터레이터를 하나의 순차적인 이터레이터로 합치고 싶을 때 사용
it = itertools.chain([1,2,3], [4,5,6])
print(list(it))
>>>
[1,2,3,4,5,6]
repeat
한 값을 계속 반복해 내놓고 싶을 때 사용. 이터레이터가 값을 내놓는 횟수를 제한하려면 repeat
의 두 번째 인자로 최대 횟수를 지정하면 된다.
it = itertools.repeat('안녕', 3)
print(list(it))
>>>
['안녕','안녕','안녕']
cycle
어떤 이터레이터가 내놓는 원소들을 계속 반복하고 싶을 때 사용
it = itertools.cycle([1,2])
result = [next(it) for _ in range(10)]
print(result)
>>>
[1,2,1,2,1,2,1,2,1,2]
tee
한 이터레이터를 병렬적으로 두 번째 인자로 지정된 개수의 이터레이터로 만들고 싶을 때 사용.
이 함수로 만들어진 각 이터레이터를 소비하는 속도가 같지 않으면, 처리가 덜 된 이터레이터의 원소를 큐에 담아둬야 하므로 메모리 사용량이 늘어난다.
it1, it2, it3 = itertools.tee(['하나','둘'], 3)
print(list(it1))
print(list(it2))
print(list(it3))
>>>
['하나','둘']
['하나','둘']
['하나','둘']
zip_longest
zip
내장 함수의 변종하으로, 여러 이터레이터 중 짧은 쪽 이터레이터의 원소를 다 사용한 경우 fillvalue
로 지정한 값을 채워 넣어준다 (기본 None
)
keys = ['하나','둘','셋']
values = [1,2]
normal = list(zip(keys, values))
print('zip:', normal)
it = itertools.zip_longest(keys, values, fillvalue='없음')
longest = list(it)
print('zip_longest:', longest)
>>>
zip: [('하나',1), ('둘',2)]
zip_longest: [('하나',1), ('둘',2), ('셋','없음')]
이터레이터에서 원소 거르기
islice
이터레이터를 복사하지 않으면서 원소 인덱스를 이용해 슬라이싱하고 싶을때 사용.
끝만 지정하거나, 시작과 끝을 지정하거나, 시작과 끝과 증가값을 지정할 수 있으며, islice
의 동작은 시퀀스 슬라이싱이나 스트라이딩 기능과 비슷하다.
values = [1,2,3,4,5,6,7,8,9,10]
first_five = itertools.islice(values, 5)
print('앞에서 다섯 개:', list(first_five))
middle_odds = itertools.islice(values, 2, 8, 2)
print('중간의 홀수들:', list(middle_odds))
>>>
앞에서 다섯 개: [1,2,3,4,5]
중간의 홀수들: [3,5,7]
takewhile
이터레이터에서 주어진 술어(predicate)가 False
를 반환하는 첫 원소가 나타날 때까지 원소를 돌려준다.
values = [1,2,3,4,5,6,7,8,9,10]
less_than_seven = lambda x: x < 7
it = itertools.takewhile(less_than_seven, values)
print(list(it))
>>>
[1,2,3,4,5,6]
dropwhile
takewhile
의 반대. 이터레이터에서 주어진 술어가 False
를 반환하는 첫 번째 원소를 찾을 때까지 이터레이터의 원소를 건너뛴다.
values = [1,2,3,4,5,6,7,8,9,10]
less_than_seven = lambda x: x < 7
it = itertools.dropwhile(less_than_seven, values)
print(list(it))
>>>
[7,8,9,10]
filterfalse
filter
내장 함수의 반대. 주어진 이터레이터에서 술어가 False
를 반환하는 모든 원소를 돌려준다.
values = [1,2,3,4,5,6,7,8,9,10]
evens = lambda x: x % 2 == 0
filter_result = filter(evens, values)
print('Filter:', list(filter_result))
filter_false_result = itertools.filterfalse(evens, values)
print('Filter false:', list(filter_false_result))
>>>
Filter: [2,4,6,8,10]
Filter false: [1,3,5,7,9]
이터레이터에서 원소의 조합 만들어내기
accumulate
파라미터를 두 개 받는 함수를 반복 적용하면서 이터레이터 원소를 값 하나로 줄여준다. (fold 연산)
values = [1,2,3,4,5,6,7,8,9,10]
sum_reduce = itertools.accuumulate(values)
print('합계:', list(sum_reduce))
def sum_modulo_20(first, second):
output = first + second
return output % 20
modulo_reduce = itertools.accumulate(values, sum_modulo_20)
print('20으로 나눈 나머지의 합계:', list(modulo_reduce))
>>>
합계: [1,3,6,10,15,21,28,36,45,55]
20으로 나눈 나머지의 합계: [1,3,6,10,15,1,8,16,5,15]
이 결과는 한 번에 한 단계씩 결과를 내놓는다는 점을 제외하면, functools
내장 모듈에 있는 reduce
함수의 결과와 같다. accumulate
에 이항 함수를 넘기지 않으면 주어진 입력 이터레이터 원소의 합계를 계산한다.
product
하나 이상의 이터레이터에 들어 있는 아이템들의 데카르트 곱(Cartesian product)을 반환한다.
리스트 컴프리헨션을 깊이 내포시키는 대신 이 함수를 사용하면 편리하다.
single = itertools.product([1,2], repeat=2)
print('리스트 한 개:', list(single))
multiple = itertools.product([1,2], ['a','b'])
print('리스트 두 개:', list(multiple))
>>>
리스트 한 개: [(1,1), (1,2), (2,1), (2,2)]
리스트 두 개: [(1,'a'), (1,'b'), (2,'a'), (2,'b')]
permutations
이터레이터가 내놓는 원소들로부터 만들어낸 길이 N인 순열을 돌려준다.
it = itertools.permutations([1,2,3,4], 2)
print(list(it))
>>>
[(1,2), (1,3), (1,4), (2,1), (2,3), (2,4), (3,1), (3,2), (3,4), (4,1), (4,2), (4,3)]
permutations
는 원본 이터레이터의 원소를 모두 다 다르다고 가정하고 순열을 만들어낸다.
combinations
이터레이터가 내놓는 원소들로부터 만들어낸 길이 N인 조합을 돌려준다.
it = itertools.combinations([1,2,3,4], 2)
print(list(it))
>>>
[(1,2), (1,3), (1,4), (2,3), (2,4), (3,4)]
combinations
역시 원본 이터레이터 원소가 모두 다 다르다고 가정하고 조합을 만들어낸다.
combinations_with_replacement
combinations
와 같지만 원소의 반복을 허용한다. (중복 조합)
it = itertools.combinations_with_replacement([1,2,3,4], 2)
print(list(it))
>>>
[(1,1), (1,2), (1,3), (1,4), (2,2), (2,3), (2,4), (3,3), (3,4), (4,4)]
기억해야 할 내용
help(itertools)
를 이용하면 더 많은 고급 함수와 추가 파라미터를 알 수 있다.