betterway36itertools

김승환·2021년 7월 11일

코딩의 기술

목록 보기
26/36

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

  • intertools 내장 모듈에는 이터레이터를 조직화하거나 사용할 때 쓸모 있는 여러 함수가 들어 있다.
  • 𝑖𝑚𝑝𝑜𝑟𝑡 𝑖𝑡𝑒𝑟𝑡𝑜𝑜𝑙𝑠
  • 복잡한 이터레이션 코드를 작성하고 있다는 사실을 깨달을 때마다 쓸만한 기능이 없는지 itertools 문서를 다시 살펴보라.

여러 이터레이터 연결하기

  • itertools 내장 모듈에는 여러 이터레이터를 하나로 합필 때 쓸 수 있는 여러 함수가 들어 있다.

chain

  • 여러 이터레이터를 하나의 순차적인 이터레이터로 합치고 싶을 때 chain을 사용
import itertools

it = itertools.chain([1, 2, 3], [4, 5, 6])
print(list(it))

[1, 2, 3, 4, 5, 6]

repeat

  • 한 값을 계속 반복해 내놓고 싶을 때 repeat을 사용.
  • 이터레이터가 값을 내놓는 횟수를 제한하려면 repeat의 두 번째 인자로 최대 횟수를 지정하면 됨.
it = itertools.repeat('안녕', 3)
print(list(it))

['안녕', '안녕', '안녕']

cycle

  • 어떤 이터레이터가 내놓는 원소들을 계속 반복하고 싶을 때는 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

  • 여러 이터레이션 중 짧은 쪽 이터레이터의 원소를 다 사용한 경우 fillvalue로 지정한 값을 채워준다.
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), ('셋', '없음')]

이터레이터에서 원소 거르기

  • itertools 내장 모듈에는 이터레이터에서 원소를 필터링할 때 쓸 수 있는 여러 함수가 들어있다.

islice

  • 이터레이터를 복사하지 않으면서 원소 인덱스를 이용해 슬라이싱 하고 싶을 때 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

  • takewhile은 이터레이터에서 주어진 술어가 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

  • 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))

filterfalse

  • 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]

이터레이터에서 원소의 조합 만들어내기

  • itertools 내장 모듈에는 이터레이터가 돌려주는 원소들의 조합을 만들어내는 몇 가지 함수가 들어 있다.

accumulate

  • accumulate는 파라미터를 두 개 받는 함수를 반복 적용하면서 이터레이터 원소를 값 하나로 줄여준다.
  • 이 함수가 돌려주는 이터레이터는 원본 이터레이터의 각 원소에 대해 누적된 결과를 내놓는다.
  • 이 결과는 한 번에 한 단계씩 결과를 내놓는다는 점을 제외하면, 기본적으로 functools 내장 모듈에 있는 reduce 결과와 같다.
  • accumlate에 이항 함수를 넘기지 않으면 주어진 입력 이터레이터 원소의 합계를 계산한다.
values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
sum_reduce = itertools.accumulate(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]

product

  • producr는 하나 이상의 이터레이터에 들어 잇는 아이템들의 데카르트 곱을 반환한다.
  • 리스트 컴프리헨션을 깊이 내포시키는 대신 이 함수를 사용하면 편리하다.
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

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)]

combinations

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_with_replacement

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)]

profile
인공지능 파이팅!

0개의 댓글