2010년 12월 18일 토요일

python yield

Generator (발생자) 는 icon이란 언어에서 나온것입니다.

여러가지 문서를 보다보면 제네레이터 이터레이터 이해할 수 없는 말들만 돌아 다닙니다

근본적으로 제네레이터 이터레이터는 무엇을 하기 위한 장치인가를 생각해보면

파이썬은 모든것을 list-sequence(수열)로 표현이 됩니다. 또 수학의 모든 수식들을 표현하기 위한 장치가 많이있습니다.

이것은 수학의 수열(sequence)의 정의와 같은 것입다 (피보나치등등)

이러한 리스트가 무한이면 어떻게 대처해야 하는가.. 에 대한 답이 제네레이터(발생자) 이터레이터(반복자)
yield(양보?) 이런것들있습니다

한글로 정확히 번역이 되지 않기에 그냥 제네레이터 이터레이터로 씁니다.

 def gen_num(N):
    for i in range(N):
        yield i

for x in gen_num(100) :
    print x,

----------------------------------------------------
0 1 2 3 4 5 6 7 8 9


사실 위의 예는 잘못된 것이다. 아래가 좀더 generator yield를 원래 의미대로 사용한 것입니다.

 def gen_num():
    while 1:
        yield i
        i++

a = gen_num()
for x in range(10) :
    print a.next(),

----------------------------------------------------
0 1 2 3 4 5 6 7 8 9


for문의 list에 gen_num으로 얻은 결과나, range(10)으로 대치를 하거나 결과는 같을 것입니다

그러면 왜 Generator방식을 사용하는가?

List로 통째로 넘기는 방식은 데이터량이 클 경우 모든 작업이 완전히 수행한 다음에 넘길수가 있습니다.
즉, 하나의 작업이 완전히 끝난후에 제어권을 넘기는 것이죠

즉, 소규모작업에서는 큰 문제가 없지만, 대형의 작업등에는 모든 작업이 끝나기를 기다릴 수가 없는 경우가 있습니다

따라서 이때는 중간 중간에 수행을 할 수 있게 yield라는 구문이 들어가게 되는 것입니다.

기존의 함수를 그대로 유지하면서 제어권만 넘기는 방식이다.

함수라는 것은 일단 실행이 되면 모든 것이 수행이 되고 끝나는 순간에 함수 내부의 모든 변수들을 반환하고

본체로 제어권을 넘겨주게 된다. 하지만, 이러한 함수가 빈번하게 발생을 한다면, 이렇게 stack방식으로 계속 반환을 한다면

이 비용도 만만치가 않게 되므로

그래서 위와 같은 Generator 방식을 사용할 수 있게 됩니다.

tree walker라든지, parser등에서 사용하면 유용할 것으로 생각됩니다.

댓글 5개:

  1. 궁금한게 있습니다...

    올려주신 두번째 코드
    def gen_num():
    while 1:
    yield i
    i++

    a = gen_num()
    for x in range(10) :
    print a.next(),

    가 정상적으로 동작하지 않는데...

    python 어떤 버전에서 가능합니까...?

    답글삭제
  2. 몇가지 의문이 들어 글 남깁니다.
    def gen_num():
    while 1:
    yield i
    i++
    위와 같은 함수 부분에 i는 지역변수로 쓰였는데 i가 최초 사용될때 초기화되지 않고 사용되는 부분에서 버전과 상관없이 인터프리터가 에러를 발생시키는게 아닌지
    또 i++이라는 연산이 python버전에 존재하는 연산자인지도 확인이 필요할 것같은데 글쓴이님의 의견이 궁굼합니다.

    답글삭제
  3. I++은 완벽한 오타입니다
    I+= 1 이 맞습니다

    답글삭제
  4. 초기화도 해주어야 하네요.

    def gen_num():
    i = 0
    while 1:
    yield i
    i += 1

    a = gen_num()
    for x in range(10) :
    print a.next(),

    답글삭제
  5. 초기화도 해주어야 하네요.

    def gen_num():
    i = 0
    while 1:
    yield i
    i += 1

    a = gen_num()
    for x in range(10) :
    print a.next(),

    답글삭제