파이썬 코딩의 기술 - 36

JinWooHyun·2021년 7월 10일
0

이터레이터나 제너레이터를 다룰 때는 itertools를 사용하라

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)를 이용하면 더 많은 고급 함수와 추가 파라미터를 알 수 있다.
profile
Unicorn Developer

0개의 댓글