python 어떻게 yield를 정확하게 사용합니까

9452 단어 pythonyield

생성기


만약 한 방법에 yield 키워드가 포함되어 있다면, 이 함수는 바로'생성기'입니다.
생성기는 사실 특수한 교체기로서, 교체기처럼 교체 출력 방법 내의 모든 원소를 교체할 수 있다.
yield 키워드를 포함하는 방법을 살펴보겠습니다.

# coding: utf8

#  
def gen(n):
    for i in range(n):
        yield i

g = gen(5)      #  
print(g)        # <generator object gen at 0x10bb46f50>
print(type(g))  # <type 'generator'>

#  
for i in g:
    print(i)
    
# Output:
# 0 1 2 3 4
이 예에서 g=gen(5)을 실행할 때gen의 코드는 실행되지 않았습니다. 이때 우리는 생성기 대상을 만들었을 뿐입니다. 그 유형은generator입니다.
그리고 for i in g를 실행할 때마다 순환을 실행하면 yield에 실행되고 yield 뒤에 있는 값을 되돌려줍니다.
이 교체 과정은 교체기와 가장 큰 차이점이다.
다시 말하면 만약에 우리가 5개의 원소를 출력하고 싶다면, 생성기를 만들 때, 이 5개의 원소는 사실 아직 생성되지 않았는데, 언제 생성됩니까?for 순환이 yield를 만났을 때만 모든 요소를 순서대로 생성합니다.
이 밖에 생성기는 교체기와 같이 교체 데이터를 실현하는 것 외에 다른 방법도 포함한다.
  • generator.__next__(): for를 실행할 때 이 방법을 사용하면 yield에 실행될 때마다 멈추고 yield 뒤에 있는 값을 되돌려줍니다. 데이터가 교체될 수 없으면 StopIterator 이상을 던져 for 순환이 끝납니다
  • generator.send(value): 외부에서 생성기 내부로 값을 전송하여 yield 앞의 값을 변경합니다
  • generator.throw(type[,value[,traceback]]): 외부에서 생성기에 이상을 던집니다
  • generator.close(): 생성기 닫기
  • 생성기를 사용하는 이런 방법을 통해 우리는 매우 많은 재미있는 기능을 완성할 수 있다.

    next


    먼저 생성기의 _next__ 방법, 우리 아래의 이 예를 봅시다.
    
    # coding: utf8
    
    def gen(n):
        for i in range(n):
            print('yield before')
            yield i
            print('yield after')
    
    g = gen(3)      #  
    print(g.__next__())  # 0
    print('----')
    print(g.__next__())  # 1
    print('----')
    print(g.__next__())  # 2
    print('----')
    print(g.__next__())  # StopIteration
    
    # Output:
    # yield before
    # 0
    # ----
    # yield after
    # yield before
    # 1
    # ----
    # yield after
    # yield before
    # 2
    # ----
    # yield after
    # Traceback (most recent call last):
    #   File "gen.py", line 16, in <module>
    #     print(g.__next__())  # StopIteration
    # StopIteration
    이 예에서, 우리는gen 방법을 정의했는데, 이 방법은yield 키워드를 포함하고 있다.그리고 우리는 g=gen(3)을 실행하여 생성기를 만들었지만, 이번에는 for를 실행하지 않고 g.__를 여러 번 호출했습니다.next__() 생성기의 요소를 출력합니다.
    g.__를 실행하면next__() 코드가 yield에 실행되고 yield 뒤에 있는 값을 되돌려줍니다. g.__ 호출을 계속하면next__(), 주의해라. 이번 실행의 시작 위치는 지난번 yield가 끝난 곳이며, 지난번 실행의 상하문을 보류하고 계속 뒤로 교체하는 것을 발견할 수 있다.
    이것이 바로 yield의 작용을 사용하는 것이다. 교체 생성기를 사용할 때 매번 실행할 때마다 이전의 상태를 유지할 수 있다. 일반적인 방법처럼return을 만나면 결과를 되돌려주고 다음 실행은 이전의 절차를 다시 반복할 수 밖에 없다.
    생성기는 상태를 보존할 수 있는 것 외에 다른 방식을 통해 내부의 상태를 바꿀 수 있다. 이것이 바로 아래에서 말하고자 하는send와throw 방법이다.

    send


    위의 예에서 우리는 yield 뒤에 값이 있는 상황만 보여 주었을 뿐, 사실은 j=yieldi라는 문법을 사용할 수 있다. 우리는 아래의 코드를 본다.
    
    # coding: utf8
    
    def gen():
        i = 1
        while True:
            j = yield i
            i *= 2
            if j == -1:
                break
    다음 코드를 실행하면 다음과 같습니다.
    
    for i in gen():
        print(i)
        time.sleep(1)
    출력 결과는 1, 2, 4, 8, 16, 32, 64...우리가 이 과정을 멈출 때까지 계속 순환해라.
    이 코드가 계속 순환하는 이유는 j===-1 이 지점에서 브레이크를 실행할 수 없기 때문이다. 만약 우리가 코드를 이 곳으로 실행시키려면 어떻게 해야 합니까?
    여기에 생성기의send방법을 사용해야 한다.send방법은 외부의 값을 생성기 내부에 전송하여 생성기의 상태를 바꿀 수 있다.
    코드는 다음과 같이 쓸 수 있습니다.
    
    g = gen()   #  
    print(g.__next__())  # 1
    print(g.__next__())  # 2
    print(g.__next__())  # 4
    # send   -1     j = -1  
    print(g.send(-1))   # StopIteration  
    우리가 g.send(-1)를 실행할 때, -1을 생성기 내부로 전송한 다음, yield 앞의 j에게 값을 부여한 것과 같다. 이때 j=-1, 그리고 이 방법은break가 나와서 계속 교체되지 않을 것이다.

    throw


    외부는 생성기 내부에 하나의 값을 전달할 수 있을 뿐만 아니라, 이상도 전달할 수 있다. 즉throw 방법을 호출한다.
    
    # coding: utf8
    
    def gen():
        try:
            yield 1
        except ValueError:
            yield 'ValueError'
        finally:
            print('finally')
    
    g = gen()   #  
    print(g.__next__()) # 1
    #    ValueError
    print(g.throw(ValueError))
    
    # Output:
    # 1
    # ValueError
    # finally
    이 예는 생성기를 만든 후 g.throw (Value Error) 방식을 사용하여 생성기 내부에 이상을 전송하여 생성기 이상 처리의 분기 논리에 이르렀다.

    close


    생성기의close방법도 비교적 간단하다. 바로 이 생성기를 수동으로 닫으면 닫은 생성기는 다시 조작할 수 없다.
    
    >>> g = gen()
    >>> g.close() #  
    >>> g.__next__() #  
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    StopIteration

    장면 사용


    yield와 생성기의 사용 방식을 이해하면 yield와 생성기는 일반적으로 어떤 업무 장면에 사용됩니까?
    다음은 내가 몇 가지 예를 소개하는데 그것이 바로 집합의 생성, 코드 구조 간소화, 협정과 병발이다. 너는 이런 사용 장면을 참고하여 yield를 사용할 수 있다.

    집합의 생성


    만약 당신이 매우 큰 집합을 만들고 싶다면list를 사용하여 집합을 만들면 메모리에 매우 큰 저장 공간을 신청할 수 있습니다. 예를 들어 다음과 같습니다.
    
    # coding: utf8
    
    def big_list():
        result = []
        for i in range(10000000000):
            result.append(i)
        return result
    
    #    
    for i in big_list():
        print(i)
    이런 장면에서 우리는 생성기를 사용하면 이 문제를 잘 해결할 수 있다.
    생성기는 yield를 실행할 때만 데이터를 교체할 수 있기 때문에 원소를 되돌려야 하는 메모리 공간만 신청할 수 있습니다. 코드는 이렇게 쓸 수 있습니다.
    
    # coding: utf8
    
    def big_list():
        for i in range(10000000000):
            yield i
    
    #      
    for i in big_list():
        print(i)

    코드 구조 간소화


    우리는 개발할 때 이런 장면을 자주 만났다. 만약에 하나의 방법이 하나의list를 되돌려야 하지만 이list는 여러 개의 논리 블록을 조합한 후에야 발생할 수 있기 때문에 우리의 코드 구조가 매우 복잡해진다.
    
    # coding: utf8
    
    def gen_list():
        #    
        result = []
        for i in range(10):
            result.append(i)
        for j in range(5):
            result.append(j * j)
        for k in [100, 200, 300]:
            result.append(k)
        return result
        
    for item in gen_list():
        print(item)
    이 경우, 우리는 모든 논리 블록에서 append를 사용하여list에 요소를 추가할 수 있을 뿐, 코드는 쓰기에 비교적 까다롭다.
    이때 만약에 yield를 사용하여 이list를 생성한다면 코드는 매우 간결하다.
    
    # coding: utf8
    
    def gen_list():
        #    yield  
        for i in range(10):
            yield i
        for j in range(5):
            yield j * j
        for k in [100, 200, 300]:
            yield k
            
    for item in gen_list():
        print(i)
    yield를 사용하면 리스트 형식의 변수를 정의할 필요가 없습니다. 논리 블록마다 yield에서 원소를 직접 되돌려주면 됩니다. 앞의 예와 같은 기능을 할 수 있습니다.
    우리는 yield를 사용하는 코드가 더욱 간결하고 구조도 더욱 뚜렷하다는 것을 보았다. 또 다른 장점은 요소가 교체될 때만 메모리 공간을 신청하여 메모리 자원의 소모를 줄이는 것이다.

    협정과 병발


    또 하나의 장면은 yield가 매우 많이 사용하는 것이다. 그것이 바로'협정과 병발'이다.
    만약에 우리가 프로그램의 집행 효율을 높이고 싶다면 일반적으로 다중 프로세스, 다중 루틴의 방식으로 프로그램 코드를 작성한다. 가장 자주 사용하는 프로그래밍 모델은 바로'생산자-소비자'모델, 즉 하나의 프로세스/루틴 생산 데이터, 다른 프로세스/루틴 소비 데이터이다.
    다중 프로세스, 다중 루틴 프로그램을 개발할 때 공유 자원이 왜곡되는 것을 방지하기 위해 우리는 통상적으로 자물쇠를 채워 보호해야 하기 때문에 프로그래밍의 복잡도를 증가시킨다.
    Python에서 프로세스와 라인을 사용하는 것 외에 우리는 코드의 운행 효율을 높이기 위해'협동'을 사용할 수 있다.
    협정이 무엇입니까?
    간단하게 말하면 여러 개의 프로그램 블록이 조합하여 협업하여 실행하는 프로그램을'협정'이라고 부른다.
    파이썬에서'협정'을 사용하려면 yield 키워드를 사용해야 합니다.
    이렇게 말하면 너무 이해할 수 있다. 우리는 yield로 협동 생산자, 소비자의 예를 실현한다.
    
    # coding: utf8
    
    def consumer():
        i = None
        while True:
            #   producer  
            j = yield i 
            print('consume %s' % j)
    
    def producer(c):
        c.__next__()
        for i in range(5):
            print('produce %s' % i)
            #   consumer
            c.send(i)
        c.close()
    
    c = consumer()
    producer(c)
    
    # Output:
    # produce 0
    # consume 0
    # produce 1
    # consume 1
    # produce 2
    # consume 2
    # produce 3
    # consume 3
    ...
    이 프로그램의 실행 절차는 다음과 같다.
  • c =consumer () 생성기 대상을 만듭니다
  • producer(c) 실행 시작, c.__next()__ 코드가 j = yield i로 실행될 때까지 생성기consumer를 시작합니다. 이 때consumer가 처음으로 실행되고 되돌아옵니다
  • producer 함수는 c.send(i)에서 생성기의send 방법을 이용하여consumer에 데이터를 보낼 때까지 계속 아래로 실행됩니다
  • consumer 함수가 깨어나서 j=yield i에서 계속 실행을 시작하고producer에서 전송된 데이터를 j에게 부여한 다음에 출력을 출력하여 다시 yield로 실행할 때까지 되돌려줍니다
  • producer는 위의 과정을 계속 순환하여 실행하고 순서대로cosnumer에게 데이터를 전송하여 순환이 끝날 때까지 한다
  • 최종 c.close()는consumer 생성기를 닫고 프로그램을 종료합니다.
  • 이 예에서 우리는 프로그램이producer와consumer 두 함수 사이를 왔다 갔다 전환하여 집행하고 서로 협력하여 생산 임무, 소비 임무의 업무 장면을 완성한 것을 발견했다. 가장 중요한 것은 전체 프로그램은 단일 프로세스의 단일 라인에서 완성된 것이다.
    이 예는 위에서 말한 yield, 생성기의 __next__、send,close 방법.만약 이해하기 어렵다면, 너는 이 예를 몇 번 더 볼 수 있으니, 스스로 시험해 보는 것이 가장 좋다.
    우리는 생산자, 소비자의 프로그램을 협동하여 작성할 때 다음과 같은 장점을 가진다.
    전체 프로그램이 실행되는 과정에서 자물쇠가 없어서 공유 변수의 보호 문제를 고려하지 않아도 되고 프로그래밍의 복잡도를 낮출 수 있다
    프로그램은 함수 사이를 왔다 갔다 전환한다. 이 과정은 사용자 상태에서 진행된 것이다. 프로세스/루틴처럼 내부 핵상태에 빠지지 않고 내부 핵상태 상하문 전환의 소모를 줄이고 집행 효율이 더욱 높다.
    그래서 Python의 yield와 생성기는 협동 프로그래밍 방식을 실현하고 프로그램의 병행 실행에 프로그래밍 기반을 제공했다.
    Python의 많은 제3자 라이브러리는 모두 이러한 특성을 바탕으로 봉인된 것이다. 예를 들어gevent,tornado 등은 프로그램의 운행 효율을 크게 향상시켰다.

    총결산


    총괄적으로 말하자면, 이 글은 우리가 주로yield의 사용 방식과 생성기의 각종 특성을 이야기했다.
    생성기는 특수한 교체기로서 데이터를 교체할 수 있을 뿐만 아니라 실행할 때 방법의 상태를 저장할 수 있다. 이외에 외부에서 내부 상태를 바꾸는 방식을 제공하여 외부의 값을 생성기 내부로 전송한다.
    yield와 생성기의 특성을 이용하여 우리는 개발에서 대집적 생성, 코드 구조 간소화, 협정과 병발 업무 장면에 사용할 수 있다.
    Python의 yield도 프로그래밍과 병발을 실현하는 기초로 프로그래밍이라는 사용자 상태의 프로그래밍 모델을 제공하여 프로그램 운행의 효율을 향상시켰다.
    이상은python이 yield를 어떻게 정확하게 사용하는지에 대한 상세한 내용입니다. 더 많은python이 yield를 사용하는 것에 관한 자료는 저희 다른 관련 글에 주목하세요!

    좋은 웹페이지 즐겨찾기