1. 리스트 컴프리헨션(List Comprehension)
- 리스트 등 순회형 컨테이너 객체로부터 이를 가공한 새로운 리스트를 생성하는 방법
- Set, Dict에 대해서도 적용 가능함.
my_list = ['a','b','c','d']
result_list = [(i,j) for i in range(2) for j in my_list]
print(result_list)
2. 리스트 컴프리헨션의 문제점
- 리스트 컴프리헨션의 가장 큰 문제점은 메모리 낭비.
- 새 리스트를 통째로 생성하기 때문에 입력이 많은 프로그램에서는 Out of memory 에러 생성.
- list의 경우 사이즈가 커질 수록 메모리 사용량이 증가, generator의 경우 사이즈가 커져도 차지하는 메모리 사이즈는 동일.
- list의 경우 모든 데이터를 메모리에 적재하여 차지하는 메모리 용량이 커지지만, generator의 경우에는 데이터 값을 호출할 때마다 차례로 값에 접근하여 메모리에 적재하기 때문.
- sys.getsizeof(): 객체의 메모리 사이즈 바이트 단위로 반환.
3. 리스트 컴프리헨션 vs 제너레이터
before = [1,2,3,4,5]
after = [v*2 for v in before if v % 2 ==0]
before = [1,2,3,4,5]
after_gene = (v*2 for v in before if v % 2 == 0)
print(after_gene)
- 내장 함수(next)를 통해 출력 가능
- 값을 호출하기 전까진 메모리에 올려져 있지 않음.
- generator expression 제공되어 ()를 사용하면 됨.
- 진행 순서는 일반 함수와 동일한 절차로 실행된다.
- yield 만나게 되면 함수를 호출했던 구문인 첫 번째 값을 반환하게 됨.
- 단, generator 함수는 유지된 상태, 그 후 for 문에 의해 다시 generator 함수가 호출됨.
- generator 함수가 처음부터 시작되는게 아니라 yield 이후 구문부터 시작하게 된다.
- i+=1-> 1이 산출됨.
- x값은 1을 전달 받고 print 된다.
4. 제너레이터 합성
- roots를 실행시킬 때, it도 함께 연쇄적으로 진행된다.
- 메모리를 효율적으로 사용하면서 이뤄지게 됨.
요약
1. 입력이 크면 out of memory 문제 방지 차, 리스트 컴프리헨션 대신 제너레이터 사용
2. 제너레이터 식은 한 번에 원소 하나씩 출력하기 때문에 메모리 문제 피할 수 있음.
3. lazy evaluation, 즉 수행 시간이 긴 계산 결과 값이 필요할 때까지 계산을 늦추는 효과를 볼 수 있다.
4. 서로 연결된 제너레이터 식은 매우 빠르게 실행, 메모리 효율적 사용함.
- 모든 iterator는 iterable 하지만, 모든 iterable가 iterator는 아니다.
- 제너레이터는 이터레이터를 생성해주는 함수.
- 이터레이터는 클래스에 iter, next또는 getitem 메서드를 구현해야 하지만 제너레이터는 함수 안에서 yield 라는 키워드만 사용하면 끝.
- 제너레이터는 이터레이터보다 훨씬 간단하게 작성 가능.
- 이터레이터는 raise로 StopIteration 에외