발생자(Generator)
발생자
발생자는 icon이란 언어에서 영향을 받았다.
만일 함수가 호출된 후에 되돌아갈 때, 메모리가 해제되지 않고 그대로 남아 있다면 어떨까? 그리고 다시 그 함수가 호출될 때 이전에 수행이 종료되었던 지점 이후를 계속 진행한다면 어떨까? => 이것이 발생자이다.
발생자란 (중단된 시점부터) 재실행 가능한 함수라고 할 수 있다.
def generate_ints(n):
for i in range(n):
yield i
yield 는 발생자를 위해서 파이썬 2.2에 새롭게 도입된 예약어이다. 어떤 함수이든 yield 를 가지고 있다면 발생자이다.
파이썬 컴파일러가 이 키워드를 검출하면 이 함수를 발생자로 만들기 위해 별도로 처리한다.
yield 는 return 과 유사하나 실행 상태를 보존한 상태에서 복귀한다.
gen 발생자 객체는 반복자이다.
generate_ints의 초기 스택 프레임이 만들어지나, 실행은 중단된 상태이다.
gen.next() 로 실행이 재개되며, yield 에 의해서 중단된다.
yield 는 일반 함수의 return 문과 같이 값을 호출 측에 돌려주나, 실행 상태와 지역 변수들을 보존한 상태로 값을 출력하는 것이 차이점이다.
발생자 객체의 next() 메소드 호출로 yield 다음 문장부터 다시 수행을 계속한다.
따라서 발생자를 다음과 같이 for 루프에 직접 사용하는 것이 가능하다.
일반 함수와 발생자의 가장 큰 차이점:
일반 함수는 호출되면, 종료할 때까지 모든 일을 마친 결과를 넘겨야 한다.
발생자는 정보를 생성하는 중간에 결과를 넘길 수 있다.
이것은 어떤 작업의 중간 결과를 다른 코드에서 참조해야 할 경우에 유용하게 활용된다. 모든 결과를 한꺼번에 받아서 처리하기보다는 중간 중간에 나오는 결과를 사용한다면 발생자가 효고적으로 활용될 수 있다.
병행 프로세스나 혹은 멀티 쓰레드로 처리해야 할 일부 작업들을 발생자로 대치할 수도 있다.
트리 탐색이나 토큰 분석기 등이 대표적으로 적용 가능한 예가 될 것 같다.
발생자 구문
파이썬 2.2에서 도입된 반복자(Iterator)로 많은 양의 자료를 처리할 때 자료를 모두 메모리에 올리지 않고 순차적으로 반복 처리하는 것이 가능하게 되었다.
리스트 내장은 리스트 객체를 새로 생성하므로 메모리를 낭비하는 문제가 있다.
리스트 내장의 대부분의 경우는 발생자 구문으로 대치될 수 있다.
중간 리스트를 생성, 저장해야 하는 경우가 아니면 발생자 구문이 대부분 더 좋은 성능을 발휘한다.
발생자의 활용
피보나치 수열
홀수 집합 만들기
※ 위의 두 개를 테스 해 본 결과, class로 만든 것이 0.5초 더 빨랐다.
list(odds(20)) 같은 경우, 가장 느렸다.
하나씩 건너뛰어서 값 취하기
혹은 itertools 모듈을 이용하면 다음과 같은 코딩도 가능하다.
트리 탐색
이진 트리에서 깊이 inorder 탐색 기법으로 모든 노드들을 탐색하는 메소드를 구현.
파이썬에서 트리를 표현하는 것은 매우 간단하다. __init__ 메소드에서처럼 좌우 노드(left, right) 그리고 데이터를 가지는 멤버로 충분히 표현된다. Iorder 탐색은 inorder 메소드로 구현된다. 이 메소드는 생성자로 구현되었다.
한 가지 주의할 것은 __iter__ 메소드이다. 이 메소드는 inorder 를 즉, inorder 생성자를 반복자로 리턴하고 있다. 트리에 대한 반복자는 곧 inorder 발생자가 된다.
따라서 마지막 두 줄처럼 마치 트리를 리스트나 튜플과 같이 수비게 for루프에서 활용할 수 있게 된다. 이ㅓㅅ으로 아주 일반화된 트리 탐색 방법을 가지게 된다. 이것은 생성자의 큰 장점이다.
중첩 리스트를 단일 리스트로 만들기
1) 발생자를 이용하지 않은 재귀적인 방법
2) 발생자를 이용한 방법
※ 발생자는 시퀀스 연산이 적용되는 곳이면 어디나 사용할 수 있다.