콘텐츠로 건너뛰기

파이썬 itertools와 함께 사용하는 간편한 방법?

[

파이썬 Itertools

파이썬에서 itertools는 파이썬 3 표준 라이브러리 중 가장 강력한 모듈 중 하나입니다. 이 모듈은 다양한 이터레이터 빌딩 블록을 구현하여, APL, Haskell, SML에서 영감을 받은 도구를 패턴으로 만들 수 있도록 합니다. 이러한 빌딩 블록들을 합쳐서 파이썬에서 효율적이고 간결한 특수 도구를 생성할 수 있도록 합니다.

본 문서에서는 itertools를 하나씩 소개하는 것이 아니라, 실제로 활용하는 방법에 대해 다룹니다. 예제들은 간단한 것에서 점점 복잡한 것으로 진행됩니다.

중급~고급 파이썬 개발자를 대상으로 하고 있으니, 이터레이터와 제너레이터, 여러 개의 변수 할당과 언패킹에 대해 자신감을 가지고 있어야 합니다. 아직 자신이 없거나 지식을 갈고 닦고 싶다면 아래 링크들을 읽어보시기 바랍니다.

준비는 되셨나요? 그러면 시작해볼까요?

Itertools란 무엇이고 어떻게 활용해야 할까요?

itertools 문서에 따르면, itertools는 “APL, Haskell, SML에서 영감을 받은 구문을 이용하여, 속도가 빠르고 메모리를 효율적으로 사용할 수 있는 특수 도구 목록을 만드는 모듈입니다.”

간단히 말하자면, itertools의 함수는 이터레이터 위에서 동작하여 보다 복잡한 이터레이터를 생성합니다. 예를 들어, 내장 zip() 함수는 임의의 개수의 이터러블 객체를 인자로 받아, 해당 객체들의 요소들로 이루어진 튜플들을 리턴하는 이터레이터를 만듭니다.

>>> list(zip([1, 2, 3], ['a', 'b', 'c']))
[(1, 'a'), (2, 'b'), (3, 'c')]

zip() 함수가 동작하는 방식은 어떻게 될까요?

zip() 함수는 받은 이터러블의 대응되는 요소들로 이루어진 튜플들을 리턴합니다. 이 과정에서 zip() 함수는 인자로 받은 이터러블들을 한 번에 하나의 요소씩 반환하도록 조절합니다. 이를 통해 두 리스트(두 이터러블)가 같은 길이를 가질 필요가 없고, 무한한 길이를 가진 이터러블들에 대해서도 잘 동작할 수 있습니다.

이처럼 zip() 함수는 itertools 내에서도 사용되는 아주 강력한 기능입니다. 이번 튜토리얼에서는 이와 유사한 기능을 가진 다양한 함수들을 알아보고, 이들을 조합하여 실용적인 예제를 만들어보겠습니다.

예제 1: grouper 함수

grouper 함수는 zip() 함수와 아주 유사한 기능을 수행하지만, 균일한 길이가 아닌 이터러블들을 처리할 때 유용합니다. 예를 들어, 세 개의 이터러블을 받아서 각 이터러블의 요소들의 세트로 이루어진 튜플을 리턴하는데, 이 세트들은 사용할 수 있는 요소가 적을 경우 None 값으로 채워집니다.

다음은 grouper 함수의 구현입니다.

from itertools import zip_longest
def grouper(iterables, fillvalue=None):
args = [iter(iterable) for iterable in iterables]
return zip_longest(*args, fillvalue=fillvalue)

from itertools import zip_longest 문장은 zip_longest() 함수를 사용하기 위해 itertools 모듈에서 해당 함수를 불러오는 것입니다. zip_longest() 함수는 zip() 함수와 유사한 기능을 수행하지만, 인자로 받은 이터러블들의 길이를 맞추기 위해 fillvalue로 지정한 값으로 빈 자리를 채웁니다.

grouper() 함수는 iterables들의 각각의 이터레이터를 zip_longest() 함수에 전달하여 해당 이터레이터들의 요소들로 채워진 튜플을 리턴합니다. 빈 자리를 채울 값으로는 fillvalue로 지정한 값을 사용합니다.

이제 grouper 함수의 사용 예제를 살펴보겠습니다.

g1 = [1, 2, 3, 4, 5]
g2 = ['a', 'b', 'c']
g3 = ['x', 'y']
result = list(grouper([g1, g2, g3]))
print(result)

결과는 다음과 같습니다.

[(1, 'a', 'x'), (2, 'b', 'y'), (3, 'c', None), (4, None, None), (5, None, None)]

이번 예제에서는 세 개의 이터러블을 받아서 길이가 가장 긴 이터러블의 길이에 맞춰서 튜플을 생성했습니다. 만약 한 이터러블의 요소의 수가 다른 이터러블들보다 적을 경우, 나머지 튜플 요소들은 None 값으로 채워지는 것을 확인할 수 있습니다.

예제 2: 짝수와 홀수 나누기

이번에는 itertools를 사용하여 짝수와 홀수로 이루어진 리스트를 분리하는 예제를 살펴보겠습니다. 리스트를 입력으로 받아 짝수와 홀수를 리스트로 반환하는 함수 partition_odds_and_evens()를 구현해보겠습니다.

from itertools import filterfalse
def partition_odds_and_evens(numbers):
evens = list(filter(lambda x: x % 2 == 0, numbers))
odds = list(filterfalse(lambda x: x % 2 == 0, numbers))
return evens, odds

이제 이 함수를 호출하여 짝수와 홀수로 이루어진 리스트를 분리해보겠습니다.

numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens, odds = partition_odds_and_evens(numbers)
print("Evens:", evens)
print("Odds:", odds)

결과는 다음과 같습니다.

Evens: [2, 4, 6, 8, 10]
Odds: [1, 3, 5, 7, 9]

filter() 함수는 리스트의 요소들을 함수로 전달하여 조건이 참인 요소들로만 이루어진 새로운 리스트를 반환합니다. lambda x: x % 2 == 0는 주어진 숫자 x가 2로 나누어 떨어지는지를 검사하는 람다 함수입니다. filterfalse()filter()와 동일한 기능을 수행하지만, 조건식이 거짓인 경우의 요소들로 이루어진 리스트를 반환합니다.

위 예제에서 partition_odds_and_evens() 함수는 주어진 리스트의 요소들을 짝수와 홀수로 분리하여 각각의 리스트로 반환합니다. 이 두 리스트를 변수에 저장하여 출력하였습니다.

이처럼 itertools 모듈을 사용하여 짝수와 홀수로 분리하는 작업을 아주 쉽게 할 수 있습니다.


이와 같이 itertools는 매우 강력하고 다양한 기능을 제공합니다. 이 모듈을 알아두고 활용하면 파이썬 개발을 보다 효율적으로 할 수 있습니다. 자세한 내용은 공식 문서를 참고하시기 바랍니다.