ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Python] Tip - comprehension이 클땐 제너레이터 표현식을 고려
    언어/파이썬 & 장고 2016. 10. 9. 17:36

    list comprehension의 문제점은 입력 시퀀스에 있는 각 값 별로 아이템을 하나씩 담은 새 리스트를 통째로 생성한다는 점입니다. 입력이 적을 땐 상관없지만 커지면 메모리를 많이 소모해서 프로그램이 다운될 수도 있습니다.

    예를 들어 파일을 읽고 각 줄에 있는 문자의 갯수를 반환한다고 하면 해당 작업을 list comprehension로 실행하면 파일에 있는 각 줄의 길이만큼 메모리가 필요합니다. 파일에 오류가 있거나 끊김 없는 네트워크 소켓일 경우 list comprehension를 사용하면 문제가 발생합니다. 아래는 입력값이 적은 경우를 list comprehension로 처리한 예입니다.

    value = [len(x) for x in open ('/tmp/my_file.txt')]
    print(value)
    # 결과
    # [100, 57, 15, ....]


    파이썬은 이 문제를 해결하고자 list comprehension와 제너레이터를 일반화한 제너레이터 표현식(generator expression)을 제공합니다. 제너레이터 표현식은 실행될 때 출력 시퀀스를 모두 구체화 (메모리에 로딩)하지 않고 표현식에서 한 번에 한 아이템을 내주는 이터레이터 (iterator)로 평가됩니다.

    제너레이터 표현식은 ()문자 사이에 list comprehension과 비슷한 문법을 사용하여 생성합니다. 다음은 이전 코드와 동일한 기능을 제너레이터 표현식으로 작성한 예입니다. 하지만 제너레이터 표현식은 즉시 이터레이터로 평가되므로 더는 진행되지 않습니다.

    it = (len(x) for x in open ('/tmp/my_file.txt'))
    print(it)
    # 결과
    # <generator object <genexpr> at 0x195e93


    필요할 때 제너레이터 표현식에서 다음 출력을 생성하려면 내장함수인 next로 반환 받은 이터레이터를 한 번씩 전진시키면 됩니다. 메모리 사용량을 걱정하지 않고 제너레이터 표현식을 사용하면 됩니다.

    print(next(it))
    print(next(it))
    # 결과
    # 100
    # 57


    제너레이터 표현식의 또 다른 장점은 다른 제너레이터 표현식과 함께 사용할 수 있다는 점입니다.

    roots = ((x, x**0.5) for x in it)


    이 이터레이터를 전진시킬 때마다 루프의 도미노 효과로 내부 이터레이터도 전진시키고 조건 표현식을 계산해서 입력과 출력을 처리합니다.

    print(next(roots))
    # 결과
    # 10.0
    # 7.54983443527


    이처럼 큰 입력 스트림에 동작하는 기능을 결합하는 방법을 찾을 때는 제너레이터 표현식이 최선의 도구입니다. 단 제너레이터 표현식이 반환한 이터레이터에는 상태가 있으므로 이터레이터를 한 번 넘게 사용하지 않도록 주의해야 합니다.

    요약

    list comprehension은 큰 입력을 처리할 때 너무 많은 메모리를 소모해서 문제를 일으킬 수 있음

    제너레이터 표현식은 이터레이터로 한 번에 한 출력만 만드므로 메모리 문제를 피할 수 있음

    한 제너레이터 표현식에서 나온 이터레이터를 또 다른 제너레이터 표현식의 for 서브 표현식으로 넘기는 방식으로 사용할 수 있음

    제너레이터 표현식은 서로 연결되어 있을 때 매우 빠르게 실행됨


    댓글