[Python Chapter1 09]
in Python on EffectivePython
본 포스팅은
파이썬코딩의기술
이라는 책을 참고하여 개인 공부를 하면서 정리하고 있습니다.문제 될 시 삭제하겠습니다.
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 서브 표현식으로 넘기는 방식으로 제너레이터 표현식을 조합할 수 있음
- 제너레이터 표현식은 서로 연결되어 있을 때 매우 빠르게 실행됨