[Python Chapter1 09]


본 포스팅은 파이썬코딩의기술 이라는 책을 참고하여 개인 공부를 하면서 정리하고 있습니다.

문제 될 시 삭제하겠습니다.

Chapter 1

9. 컴프리헨션이 클 때는 제너레이터 표현식을 고려하자

  • 리스트 컴프리헨션의 문제점은 입력 시퀀스에 있는 각 값별로 아이템을 하나씩 담은 새 리스트를 통째로 생성함
  • 입력이 적을 때는 괜찮지만 클 때는 메모리를 많이 소모해서 프로그램을 망가뜨리는 원인
  • 예를 들어, 파일을 읽고 각 줄에 있는 문자의 개수를 반환한다고 가정
    • 이 작업을 리스트 컴프리헨션으로 하면 파일에 있는 각 줄의 길이만큼 메모리가 필요
    • 파일에 오류가 있거나 끊김이 없는 네트워크 소켓일 경우 리스트 컴프리헨션을 사용하면 문제가 발생
# 입력값이 적은 경우만 처리 할 수 있는 방식으로 리스트 컴프리헨션을 사용 한 예시
value = [len(x) for x in open('/tmp/my_file.txt')]
print(value)

>>>
[100, 57, 15, 1, 12, 75, 5, 86, 89, 11]

파이썬은 이 문제를 해결하려고 리스트 컴프리헨션과 제너레이터를

일반화한 제너레이터 표현식(generator expression)을 제공

제너레이터 표현식은 실행될 때 시퀀스를 모두 구체화(여기서는 메모리에 로딩)하지 않음

대신 표현식에서 한 번에 한 아이템을 내주는 이터레이터로 평가

  • 이터레이터(iterator)란?
    • next()를 호출할 때 다음값을 생성해내는 상태를 가진 헬퍼 객체
    • next()를 가진 모든 객체는 이터레이터
    • 따라서, 제너레이터는 이터레이터다. 즉 제너레이터는 이터레이터를 상속

제너레이터 표현식은 리스트 컴프리헨션 문법과 비슷한데 차이점은 [] 대신에 ()를 사용

다음은 같은 예를 제너레이터 표현식으로 작성한 예

하지만 제너레이터 표현식은 즉시 이터레이터로 평가되므로 더는 진행되지 않는다

it = (len(x) for x in open('/tmp/my_file.txt')

print(it)
>>>
<generator object <genexpr> at 0x101b81480>

필요할 때 제너레이터 표현식에서 다음 출력을 생성하려면 내장 함수 next로 반환받은 이터레이터를 한 번에 전진시키면 된다

코드에서는 메모리 사용량을 걱정하지 않고 제너레이터 표현식을 사용하면 됨

print(next(it))
print(next(it))

>>>
100
57

제너레이터 표현식의 또 다른 강력한 결과는 다른 제너레이터 표현식과 함께 사용할 수 있다는 점

다음은 앞의 제너레이터 표현식이 반환한 이터레이터를 다른 제너레이터 표현식의 입력으로 사용한 예시

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

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

print(next(roots))

>>>
(15, 3.872983346207417)

이처럼 제너레이터를 연결하면 파이썬에서 매우 빠르게 실행할 수 있음

큰 입력 스트림에 동작하는 기능을 결합하는 방법을 찾을 때는 제너레이터 표현식이 최선의 도구

단 제너레이터 표현식이 반환한 이터레이터는 상태가 있으므로 이터레이터를 한 번 넘게 사용하지 않도록 주의

정리

  • 리스트 컴프리헨션은 큰 입력을 처리할 때 너무 많은 메모리를 소모해서 문제를 일으킬 수 있음
  • 제너레이터 표현식은 이터레이터로 한 번에 한 번만 만드므로 메모리 문제를 피할 수 있음
  • 한 제너레이터 표현식에서 나온 이터레이터를 또 다른 제너레이터 표현식의 for 서브 표현식으로 넘기는 방식으로 제너레이터 표현식을 조합할 수 있음
  • 제너레이터 표현식은 서로 연결되어 있을 때 매우 빠르게 실행됨





© 2018. by Dev_Yonghee

Powered by Dev_Yong