Effective Python Item 32. 긴 리스트 컴프리헨션보다는 제네레이터 식을 사용하라.

    들어가기 전


    요약

    • 리스트 컴프리헨션에서 입력이 크면 메모리 문제가 발생할 수 있음. 
    • 제네레이터 식은 한 번에 제네레이터가 반환되기 때문에 메모리를 효율적으로 사용할 수 있음. 
    • 제네레이터 식에 다른 제네레이터를 내포할 수 있음. 이를 통해 여러 제네레이터를 하나로 합성할 수 있음. 
      • 각 제네레이터는 상태를 가지기 때문에 이 부분을 유의해서 사용해야 함.
    • 합성된 제네레이터 식은 매우 빠르게 실행되며 메모리도 효율적으로 사용함. 

     


    Item 32. 긴 리스트 컴프리헨션보다는 제네레이터 식을 사용하라.

    # 리스트 컴프리헨션 사용 예시
    # my_file.txt의 라인 수가 수십 조라면? → 메모리 터짐. 
    value = [len(x) for x in open('my_file.txt')]
    print(value)

    리스트 컴프리헨션의 문제점은 메모리 사용량과 관련이 있다. 리스트 컴프리헨션은 만들어 둔 객체를 모두 리스트에 보관하기 때문에 그만큼 많은 메모리를 사용한다. 만약 너무 많은 메모리를 사용하게 되면 프로그램이 종료될 수 있다. 

    위 코드에서 my_file.txt 파일이 수십 조의 라인으로 이루어져 있다고 가정해보자. 그러면 바로 메모리 부족으로 프로그램이 종료될 것이다.

    # 제네레이터 식 사용해서 메모리 효율화
    # 컴프리헨션에서 ()를 사용하면 제네레이터가 반환됨. 
    it = (len(x) for x in open('my_file.txt'))
    print(it)
    
    # next()로 다음 호출
    next(it)
    next(it)
    
    # for문으로 다음 호출. 
    for v in it:
      print(v)

    가능하다면 제네레이터 식을 사용해서, 리스트 컴프리헨션과 비슷한 효과를 내면서 메모리를 효율적으로 사용해 볼 수 있다. 

    • 제네레이터 식의 결과로 제네레이터(이터레이터)가 반환됨.
    • 이터레이터가 다음 인자로 넘어가려면 next()를 호출해야함. 
    it = (len(x) for x in open('my_file.txt'))
    
    # 제네레이터 식 안에 제네레이터가 내포됨.
    roots = ((x, x**0.5) for x in it)
    
    print(it)
    print(roots)
    print(next(it))
    print(next(it))
    print(next(roots))

    또한 리스트 컴프리헨션을 여러 번 내포할 수 있는 것처럼 제네레이터 식에도 제네레이터를 내포할 수 있다.  

    • roots 제네레이터에 next()를 호출하면, next(it)가 호출되면서 x를 획득하게 되고 (x, x**0.5)가 반환된다. 
    • 이터레이터는 상태를 가진다. 따라서 next(it)를 호출한 후에 next(roots)를 호출하게 되면, roots는 특정 값을 놓치게 된다.
      • it에 대해서 전체 이터레이팅을 했을 때, [10, 9, 8, 7]을 받을 수 있다고 가정해보자.
      • next(it) → next(roots)를 호출하게 되면, 10은 next(it)가 호출되었을 때 사용되었고, next(roots)에서는 9라는 값을 받게 된다. 

    댓글

    Designed by JB FACTORY