[Python]Generator

Jay·2023년 1월 3일
0
post-thumbnail

Generator란?

파이썬3 버전에서는 generator를 'generator iterator를 반환하는 함수'라고 정의하고 있습니다. generator에 관련된 용어를 정리하면 generator 함수와 generator iterator 두가지로, 일반적으로 generator라고 표현하는 것은 generator 함수를 의미합니다.

Generator Iterator

일반적으로 generator iterator가 iterator의 속성을 가지고 있기 때문에 이 둘을 명확히 구분하여 사용하지는 않습니다. 하지만 이 두 iterator의 next 메서드 정의를 보면 차이가 존재합니다.

  • iterator의 next : Container에 있는 다음 항목을 반환한다.
  • generator의 next : generator 함수를 실행하거나 마지막으로 실행된 yield 구문에서 다시 시작한다.

다른 말로 풀이해본다면 iterator는 연산을 모두 수행한 뒤, 결과를 저장하여 사용합니다. 하지만 generator iterator의 경우에는 값을 반환할 때 연산을 수행하여 값을 구하는 방식으로 실행됩니다. iterator는 연산한 결과를 메모리에 저장하여 하나씩 꺼내오는 것이지만, generator iterator가 값을 반환할 때마다 연산을 수행하는 특징을 가지는 것입니다. 이러한 차이는 yield라는 키워드로 인하여 발생하게 됩니다.


Yield

yield 구문은 generator를 정의하고 로직의 수행을 조절하는 역할을 수행합니다. generator에서 yield 구문을 만나면 실행을 멈추고, generator가 다시 호출되면 yield에서부터 다시 코드가 실행됩니다. 이처럼 yield문을 만나게 되면 현재의 상태가 멈추어 유지되게 되는데, 이때 지역변수나 실행 중인 위치, 내부 실행 스택 등이 모두 저장됩니다.

yield로 값을 입력/반환하기
yield는 generator의 실행상태를 멈출뿐 아니라 generator로 값을 입력하거나 반환 받는 기능도 수행합니다.

# generator 함수
def gen():
	val = 2
    
    while True:
    	print(val)
        val = yield
        
g = gen()
next(g)		# 2 출력
g.send(3)	# 3 출력
g.send(5)	# 5 출력

위의 예제에서 next 메소드와 send 메소드를 통해 generator를 호출하고 값을 전송했습니다. 이와 같이 generator를 응용하면 함수들 간의 호출 흐름을 제어할 수 있고, 데이터도 전달할 수 있습니다.


Stateful Generator

앞에서 generator는 상태가 멈추면 지역변수와 실행 중인 위치, 내부 실행 스택 등이 모두 유지된다고 했습니다. 이처럼 상태를 저장하는 기능은 generator의 가장 중요한 내용 중 하나입니다.

def gen(items):
	count = 0
    
    for item in items:
    	if count >= 10:
        	return -1
        
        count += 1
        yield item

g = gen(range(15))
for i in g:
	print(i)		# 0-9 까치 순서대로 출력된다.

items 에서 10개의 item을 yield하고 이후에는 -1을 return하는 generator iterator입니다. generator iterator에서 return문을 실행하게 되면 StopIteration 예외가 발생하게 됩니다. generator iterator는 지역변수, 코드 실행위치에 관한 상태를 지니기 때문에 한번 예외가 발생하게 된다면 예외 상태를 유지하게 됩니다. 따라서 next나 send 메소드를 사용하여 코드를 다시 실행하여도 반복해서 StopIteration 예외를 반환하게 됩니다.

Generator 성능

Generator는 Iterator가 사용되는 모든 환경을 대체할 수 있습니다. 기본적으로 Generator Iterator가 Iterator의 속성을 가지기 때문입니다. Iterator는 모든 연산을 완료한 뒤 메모리에 값을 저장하여 하나씩 사용하고, Generator는 값이 필요할 때마다 연산을 수행하기 때문에 메모리의 사용량이 적다고 했습니다. 메모리의 효율성은 generator가 효율적인 것은 확인하였습니다.

[Time]Iterator 	0.8530163000000002
[Time]Generator 0.007532000000000006

100,000개의 리스트를 생성하여 평균을 구하는 연산을 측정한 실행 시간입니다. 정확한 측정은 아니지만, generator가 iterator에 비해 빠른 실행 시간을 가지고 있습니다.


Lazy Evaluation

laze evaluation은 코드의 실행이 필요할때까지 실행하지 않고 지연하였다가 필요한 때에 실행하도록 하는 프로그래밍 기법입니다. 이와 같은 기법은 불필요한 실행을 줄일 수 있고, 잠재적으로 무한 데이터 구조를 정의할 수 있습니다. 당장 실행하지 않으니 데이터의 길이의 제약을 받지 않아 스트림과 같은 처리에서 유용하게 사용될 수 있습니다. 이에 관한 내용은 후에 Comprehension을 통해 알아보도록 하겠습니다.


0개의 댓글