Effective Python Item 36. Iterator / Generator를 다룰 때는 itertools를 사용하라

    들어가기 전

    • 이 글은 이펙티브 파이썬을 공부하며 작성한 글입니다. 

     


    1. Itertools 라이브러리 

    Itertools 라이브러리에는 이터레이터를 위한 몇 가지 기능들이 추가되어있다. 이터레이터와 관련된 코드가 복잡해지는 경우라면, itertools 라이브러리에서 원하는 기능을 지원하는지 살펴보는 것이 좋을 수 있다.

     


    2. 여러 이터레이터 연결하기 

    itertools에는 여러 이터레이터를 하나로 합치는데 사용할 수 있는 함수들이 지원된다. 

     

    2.1 chain() 

    it = itertools.chain([1,2,3], [4,5,6]) 
    print(list(it))
    
    >>> [1, 2, 3, 4, 5, 6]
    • 여러 이터레이터를 하나의 이터레이터를 합침. 

     

    2.2 repeat() 

    # 3번만 반복하는 Iterator 생성 
    it = itertools.repeat('hello', 3)
    print(list(it))
    >>> ['hello', 'hello', 'hello']
    
    # 무한히 반복하는 iterator 생성
    c = 2
    it = itertools.repeat('hello')
    for i in it:
      print(i)
      if (c := c-1) == 0 :
        break
    >> hello
    >> hello
    • 한 값을 계속 반복해서 사용하고 싶을 때, repeat()를 사용한다. 
    • 2번째 인자에 최대 반복횟수를 정할 수 있음. 정하지 않는 경우 무한히 반복함. 

     

    2.3. cycle() 

    import itertools
    
    it = itertools.cycle([1, 2, 3])
    result = [next(it) for _ in range(10)]
    print(result)
    
    >>> [1, 2, 3, 1, 2, 3, 1, 2, 3, 1]
    • 특정 값들을 규칙적으로 반복하는 이터레이터를 만들고 싶을 때 사용할 수 있음. 

     

    2.4. tee

    it1, it2, it3 = itertools.tee(['one', 'two'], 3)
    print(list(it1))
    print(list(it2))
    print(list(it3))
    
    >>> ['one', 'two']
    >>> ['one', 'two']
    >>> ['one', 'two']
    • 한 이터레이터를 이용해 동일한 여러 이터레이터를 만들고 싶을 때, tee()를 사용한다. 
    • 이렇게 만들어진 이터레이터의 소모 속도가 동일하지 않은 경우, 아직 사용되지 않은 이터레이터의 원소를 보관해야하기 때문에 메모리 사용량이 늘어날 수 있다.

     

    2.5. zip_longest

    import itertools
    
    keys = ['a', 'b', 'c']
    values = [1, 2]
    
    # 여러 이터레이터 동시에 이터레이팅. 
    it = itertools.zip_longest(keys, values, fillvalue="no value")
    print(list(it))
    
    >>>
    [('a', 1), ('b', 2), ('c', 'no value')]
    • 여러 이터레이터를 동시에 이터레이팅 하고 싶을 때 사용함. 
    • 긴 이터레이터를 기준으로 이터레이팅함. 
      • 이 때 짧은 이터레이터에는 fillvalue를 기준으로 값을 채워넣어줌. fillvalue의 기본값은 None임. 

     


    3. 이터레이터에서 원소 필터링 

    이터레이터에서 원소 필터링을 하는 함수가 지원된다. 

     

    3.1 islice() 

    import itertools
    
    values = [n for n in range(1, 11)]
    
    # stop = 5
    it1 = itertools.islice(values, 5)
    print(list(it1))
    
    # start, stop, step
    it2 = itertools.islice(values, 0, 5, 2)
    print(list(it2))
    
    
    >>
    [1, 2, 3, 4, 5]
    [1, 3, 5]
    • 이터레이터를 복사하지 않으면서 원소 인덱스를 이용해 슬라이싱 하고 싶을 때, islice()를 사용하면 됨.
      • stop만 지정하는 경우
      • start, stop, step을 지정하는 경우

    위처럼 사용할 수 있음. 

     

    3.2 takewhile() 

    import itertools
    
    values = [n for n in range(1, 11)]
    less_than_seven = lambda x : x < 7
    it = itertools.takewhile(less_than_seven, values)
    
    print(list(it))
    
    >>> 
    [1, 2, 3, 4, 5, 6]
    • takewhile은 각 원소마다 주어진 함수로 원소를 평가하고, False를 처음 반환하는 원소를 만나는 경우 종료됨. 
    • False를 처음 반환하는 원소 이전의 원소들을 이터레이터로 반환함. 

     

     

    3.3 dropwhile() 

    import itertools
    
    values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 6, 5, 4, 3, 2, 1]
    less_than_seven = lambda x: x < 7
    it = itertools.dropwhile(less_than_seven, values)
    
    print(list(it))
    >>>
    [7, 8, 9, 10, 11, 6, 5, 4, 3, 2, 1]
    • dropwhile은 각 원소마다 주어진 함수로 원소를 평가하고, False를 처음 반환하는 원소를 찾을 때까지 모든 원소를 건너띔. 
    • False를 처음 반환하는 원소를 찾으면 그 원소 뒷쪽의 원소들 모두를 이터레이터로 반환함. (뒷쪽은 만족하지 않아도 상관없음) 

     

    3.4 filterfalse

    import itertools
    
    values = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 6, 5, 4, 3, 2, 1]
    less_than_seven = lambda x: x < 7
    it = itertools.filterfalse(less_than_seven, values)
    
    print(list(it))
    
    >>>
    [7, 8, 9, 10, 11]
    • 내장 함수 Filter는 표현식이 true인 원소만 반환한다.
    • filterfalse는 표현식이 false인 원소만 반환한다.

     

     

    댓글

    Designed by JB FACTORY