it-swarm-ko.com

"yield"키워드는 무엇을합니까?

파이썬에서 yield 키워드를 사용하는 것은 무엇입니까? 그것은 무엇을합니까?

예를 들어이 코드를 이해하려고합니다. 1 :

def _get_child_candidates(self, distance, min_dist, max_dist):
    if self._leftchild and distance - max_dist < self._median:
        yield self._leftchild
    if self._rightchild and distance + max_dist >= self._median:
        yield self._rightchild  

그리고 이것은 호출자입니다.

result, candidates = [], [self]
while candidates:
    node = candidates.pop()
    distance = node._get_dist(obj)
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result

_get_child_candidates 메서드가 호출되면 어떻게됩니까? 목록이 반환 되었습니까? 단일 요소? 다시 부르니? 후속 통화는 언제 중단됩니까?


1.이 코드는 Jochen Schulz (jrschulz)가 작성한 것으로, Python 라이브러리를 훌륭한 공간으로 만들었습니다. 이것은 전체 소스에 대한 링크입니다 : Module mspace .

9208
Alex. S.

yield의 기능을 이해하려면 발전기 가 무엇인지 이해해야합니다. 그리고 생성기를 이해하기 전에 iterables 를 이해해야합니다.

이터 러블

목록을 만들면 항목을 하나씩 읽을 수 있습니다. 항목을 하나씩 읽는 것을 반복이라고합니다.

>>> mylist = [1, 2, 3]
>>> for i in mylist:
...    print(i)
1
2
3

mylist 반복 가능 입니다. 리스트 이해를 사용하면리스트를 작성하므로 반복 가능합니다.

>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
...    print(i)
0
1
4

"for... in..."에서 사용할 수있는 모든 것은 반복 가능합니다. lists, strings, 파일 ...

이 iterable은 원하는만큼 읽을 수 있기 때문에 편리하지만 모든 값을 메모리에 저장하므로 많은 값을 가질 때 항상 원하는 것은 아닙니다.

발전기

제너레이터는 반복자입니다. 반복 가능한 종류의 한 번만 반복 할 수 있습니다 . 생성기는 메모리에 모든 값을 저장하지 않습니다. 그들은 즉시 값을 생성합니다 :

>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
...    print(i)
0
1
4

() 대신 []을 사용한 것을 제외하고는 동일합니다. 그러나 생성자는 한 번만 사용할 수 있기 때문에 cannot for i in mygenerator을 다시 수행하십시오. 하나씩.

수율

yield은 함수가 생성기를 반환한다는 점을 제외하고 return과 같이 사용되는 키워드입니다.

>>> def createGenerator():
...    mylist = range(3)
...    for i in mylist:
...        yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
...     print(i)
0
1
4

여기서는 쓸모없는 예이지만 함수가 한 번만 읽어야 할 거대한 값 집합을 반환한다는 것을 알면 편리합니다.

yield을 마스터하려면 함수를 호출 할 때 함수 본문에 작성한 코드가 실행되지 않습니다. 함수는 생성기 객체 만 반환한다는 것을 이해해야합니다. , 이것은 약간 까다 롭습니다 :-)

그러면 for에서 생성기를 사용할 때마다 중단 된 지점부터 코드가 계속됩니다.

이제 어려운 부분 :

for이 (가) 함수에서 생성 된 생성기 객체를 처음 호출하면 yield에 도달 할 때까지 함수의 코드를 처음부터 실행 한 다음 루프의 첫 번째 값을 반환합니다. 그런 다음 서로 호출하면 함수에 작성한 루프를 한 번 더 실행하고 반환 할 값이 없을 때까지 다음 값을 반환합니다.

함수가 실행되면 생성기는 비어있는 것으로 간주되지만 더 이상 yield에 도달하지 않습니다. 루프가 종료되었거나 더 이상 "if/else"을 (를) 만족시키지 못하기 때문일 수 있습니다.


코드 설명

발전기:

# Here you create the method of the node object that will return the generator
def _get_child_candidates(self, distance, min_dist, max_dist):

    # Here is the code that will be called each time you use the generator object:

    # If there is still a child of the node object on its left
    # AND if distance is ok, return the next child
    if self._leftchild and distance - max_dist < self._median:
        yield self._leftchild

    # If there is still a child of the node object on its right
    # AND if distance is ok, return the next child
    if self._rightchild and distance + max_dist >= self._median:
        yield self._rightchild

    # If the function arrives here, the generator will be considered empty
    # there is no more than two values: the left and the right children

방문객:

# Create an empty list and a list with the current object reference
result, candidates = list(), [self]

# Loop on candidates (they contain only one element at the beginning)
while candidates:

    # Get the last candidate and remove it from the list
    node = candidates.pop()

    # Get the distance between obj and the candidate
    distance = node._get_dist(obj)

    # If distance is ok, then you can fill the result
    if distance <= max_dist and distance >= min_dist:
        result.extend(node._values)

    # Add the children of the candidate in the candidates list
    # so the loop will keep running until it will have looked
    # at all the children of the children of the children, etc. of the candidate
    candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))

return result

이 코드에는 여러 가지 스마트 한 부분이 있습니다.

  • 루프는 목록에서 반복되지만 루프가 반복되는 동안 목록이 확장됩니다. :-) 무한 루프로 끝날 수 있기 때문에 조금 위험하지만 중첩 된 모든 데이터를 통과하는 간결한 방법입니다. 이 경우 candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))은 생성기의 모든 값을 소진하지만 while은 동일한 노드에 적용되지 않으므로 이전 생성기와 다른 값을 생성하는 새로운 생성기 객체를 계속 생성합니다.

  • extend() 메소드는 iterable을 예상하고 해당 값을 목록에 추가하는리스트 오브젝트 메소드입니다.

일반적으로 목록을 전달합니다.

>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]

그러나 코드에서 생성기를 얻습니다.

  1. 값을 두 번 읽을 필요는 없습니다.
  2. 자녀가 많을 수 있으며 자녀가 모두 메모리에 저장되는 것을 원하지 않습니다.

그리고 Python은 메소드의 인수가 목록인지 여부를 신경 쓰지 않기 때문에 작동합니다. Python은 이터 러블을 기대하므로 문자열,리스트, 튜플 및 생성기에서 작동합니다! 이것을 오리 타이핑이라고하며 Python이 너무 멋진 이유 중 하나입니다. 그러나 이것은 또 다른 질문입니다.

여기에서 멈추거나 약간만 읽으면 발전기의 고급 사용법을 볼 수 있습니다.

발전기 피로 제어

>>> class Bank(): # Let's create a bank, building ATMs
...    crisis = False
...    def create_atm(self):
...        while not self.crisis:
...            yield "$100"
>>> hsbc = Bank() # When everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # Crisis is coming, no more money!
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # It's even true for new ATMs
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # Build a new one to get back in business
>>> for cash in brand_new_atm:
...    print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...

참고 : Python 3의 경우 print(corner_street_atm.__next__()) 또는 print(next(corner_street_atm))

리소스에 대한 액세스 제어와 같은 다양한 작업에 유용 할 수 있습니다.

Itertools, 가장 친한 친구

Itertools 모듈에는 iterable을 조작하는 특수 함수가 포함되어 있습니다. 발전기를 복제하고 싶습니까? 두 발전기를 연결? 하나의 라이너로 중첩 목록의 값을 그룹화합니까? Map / Zip 다른 목록을 만들지 않습니까?

그런 다음 import itertools입니다.

예를 들어? 4 마리 경주에 가능한 도착 순서를 보자.

>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
 (1, 2, 4, 3),
 (1, 3, 2, 4),
 (1, 3, 4, 2),
 (1, 4, 2, 3),
 (1, 4, 3, 2),
 (2, 1, 3, 4),
 (2, 1, 4, 3),
 (2, 3, 1, 4),
 (2, 3, 4, 1),
 (2, 4, 1, 3),
 (2, 4, 3, 1),
 (3, 1, 2, 4),
 (3, 1, 4, 2),
 (3, 2, 1, 4),
 (3, 2, 4, 1),
 (3, 4, 1, 2),
 (3, 4, 2, 1),
 (4, 1, 2, 3),
 (4, 1, 3, 2),
 (4, 2, 1, 3),
 (4, 2, 3, 1),
 (4, 3, 1, 2),
 (4, 3, 2, 1)]

반복의 내부 메커니즘 이해

반복은 이터 러블 (__iter__() 메소드 구현) 및 반복자 (__next__() 메소드 구현)를 암시하는 프로세스입니다. 이터 러블은 이터레이터를 얻을 수있는 객체입니다. 이터레이터는 이터 러블을 반복 할 수있는 객체입니다.

이 기사에서 for 루프 작동 방식 에 대한 자세한 내용이 있습니다.

13704
e-satis

yield 이해에 대한 바로 가기

yield 문이있는 함수가 표시되면이 간단한 트릭을 적용하여 어떤 일이 발생하는지 이해하십시오.

  1. 함수 시작시 result = [] 줄을 삽입하십시오.
  2. yield exprresult.append(expr)으로 바꾸십시오.
  3. 함수 하단에 return result 줄을 삽입하십시오.
  4. 예, 더 이상 yield 문이 없습니다! 코드를 읽고 파악하십시오.
  5. 함수를 원래 정의와 비교하십시오.

이 트릭은 함수의 논리에 대한 아이디어를 줄 수 있지만 yield에서 실제로 발생하는 것은 목록 기반 접근 방식에서 발생하는 것과 크게 다릅니다. 많은 경우 수율 접근 방식은 훨씬 더 효율적이고 빠른 메모리입니다. 다른 경우에는이 기능을 사용하면 원래 기능이 제대로 작동하더라도 무한 루프에 빠질 수 있습니다. 자세한 내용은 계속 읽으십시오 ...

Iterables, Iterators 및 Generators를 혼동하지 마십시오

먼저, 쓸 때 반복자 프로토콜-

for x in mylist:
    ...loop body...

파이썬은 다음 두 단계를 수행합니다.

  1. mylist에 대한 반복자를 가져옵니다.

    iter(mylist)->을 호출하면 next() 메소드 (또는 Python 3의 __next__())가있는 객체가 반환됩니다.

    [이것은 대부분의 사람들이 당신에게 말하지 않는 단계입니다]

  2. 반복자를 사용하여 항목을 반복합니다.

    1 단계에서 리턴 된 반복자에서 next() 메소드를 계속 호출하십시오. next()의 리턴 값이 x에 지정되고 루프 본문이 실행됩니다. next() 내에서 StopIteration 예외가 발생하면 반복기에 더 이상 값이없고 루프가 종료 된 것입니다.

진실은 Python은 객체의 내용을 루프 오버 하고자 할 때마다 위의 두 단계를 수행한다는 것입니다. for 루프이지만 otherlist.extend(mylist)과 같은 코드 일 수도 있습니다 (여기서 otherlist은 Python 목록 임).

여기서 mylist은 반복자 프로토콜을 구현하기 때문에 iterable 입니다. 사용자 정의 클래스에서 __iter__() 메소드를 구현하여 클래스의 인스턴스를 반복 가능하게 만들 수 있습니다. 이 메소드는 반복자 를 반환해야합니다. 반복자는 next() 메소드를 가진 객체입니다. 동일한 클래스에서 __iter__()next()을 모두 구현할 수 있으며 __iter__()에서 self을 리턴 할 수 있습니다. 간단한 경우에는 작동하지만 두 개의 반복자가 동일한 객체에서 동시에 반복되도록하려는 경우에는 그렇지 않습니다.

이것이 반복자 프로토콜이므로 많은 객체가이 프로토콜을 구현합니다.

  1. 내장 목록, 사전, 튜플, 세트, ​​파일.
  2. __iter__()을 구현하는 사용자 정의 클래스.
  3. 발전기.

for 루프는 어떤 종류의 객체를 처리하는지 알지 못합니다. 반복자 프로토콜을 따르기 만하면 next()을 호출 할 때 항목 다음에 항목을 가져옵니다. 내장 목록은 항목을 하나씩 반환하고 사전은 를 하나씩 반환하고 파일은 줄을 반환 하나씩 하나씩 생성합니다. 그리고 제너레이터가 돌아옵니다 ... yield이있는 곳 :

def f123():
    yield 1
    yield 2
    yield 3

for item in f123():
    print item

yield 문 대신 f123()에 세 개의 return 문이 있으면 첫 번째 만 실행되고 함수는 종료됩니다. 그러나 f123()은 일반적인 기능이 아닙니다. f123()이 호출되면, not yield 는 yield 문에 값을 반환하지 않습니다! 생성기 객체를 반환합니다. 또한 함수는 실제로 종료되지 않으며 일시 중단 상태가됩니다. for 루프가 생성기 객체를 반복하려고하면 함수는 이전에 반환 된 yield 바로 다음 줄에서 일시 중단 상태에서 다시 시작되고이 경우 다음 코드 줄을 실행합니다. yield 문으로 다음 항목으로 반환합니다. 이것은 함수가 종료 될 때까지 발생하며,이 시점에서 제너레이터가 StopIteration을 높이고 루프가 종료됩니다.

따라서 생성기 객체는 일종의 어댑터와 비슷합니다. 한쪽 끝에는 for 루프를 행복하게 유지하기 위해 __iter__()next() 메서드를 노출시켜 반복자 프로토콜이 표시됩니다. 그러나 다른 쪽 끝에서는 다음 값을 가져 오기에 충분할 정도로 함수를 실행하고 일시 중단 모드로 되돌립니다.

왜 발전기를 사용해야합니까?

일반적으로 생성기를 사용하지 않지만 동일한 논리를 구현하는 코드를 작성할 수 있습니다. 한 가지 옵션은 앞에서 언급 한 임시 목록 '트릭'을 사용하는 것입니다. 예를 들어 모든 경우에 작동하지는 않습니다. 무한 루프가 있거나 목록이 길면 메모리를 비효율적으로 사용할 수 있습니다. 다른 방법은 인스턴스 멤버의 상태를 유지하고 next() (또는 Python의 __next__())에서 다음 논리적 단계를 수행하는 새로운 반복 가능 클래스 SomethingIter을 구현하는 것입니다. ) 방법. 논리에 따라 next() 메소드 내부의 코드는 매우 복잡해 보이고 버그가 발생하기 쉽습니다. 여기서 발전기는 깨끗하고 쉬운 솔루션을 제공합니다.

1845
user28409

이것을 다음과 같이 생각하십시오.

Iterator는 next () 메서드가있는 객체에 대한 멋진 말입니다. 그래서 yield-ed 함수는 다음과 같이 끝납니다 :

원본 버전 :

def some_function():
    for i in xrange(4):
        yield i

for i in some_function():
    print i

이것은 기본적으로 파이썬 인터프리터가 위의 코드로 수행하는 작업입니다.

class it:
    def __init__(self):
        # Start at -1 so that we get 0 when we add 1 below.
        self.count = -1

    # The __iter__ method will be called once by the 'for' loop.
    # The rest of the magic happens on the object returned by this method.
    # In this case it is the object itself.
    def __iter__(self):
        return self

    # The next method will be called repeatedly by the 'for' loop
    # until it raises StopIteration.
    def next(self):
        self.count += 1
        if self.count < 4:
            return self.count
        else:
            # A StopIteration exception is raised
            # to signal that the iterator is done.
            # This is caught implicitly by the 'for' loop.
            raise StopIteration

def some_func():
    return it()

for i in some_func():
    print i

배후에서 무슨 일이 벌어지고 있는지 더 자세히 알기 위해 for 루프를 다음과 같이 다시 작성할 수 있습니다.

iterator = some_func()
try:
    while 1:
        print iterator.next()
except StopIteration:
    pass

그게 더 이해가 되니 아니면 더 많이 혼란 스럽습니까? :)

나는 이것을 is 설명의 목적을 위해 지나치게 단순화 한 것에 주목해야한다. :)

445
Jason Baker

yield 키워드는 두 가지 간단한 사실로 축소되었습니다.

  1. 컴파일러가 함수 내에서 yield 키워드 anywhere 를 감지하면 해당 함수는 더 이상 return 문을 통해 반환되지 않습니다. 대신 대신에 생성기라고하는 게으른 "보류 목록"개체 를 반환합니다.
  2. 생성기는 반복 가능합니다. iterable 이란 무엇입니까? list 또는 set 또는 range 또는 dict-view와 같은 것으로, 특정 순서로 각 요소를 방문하기위한 내장 프로토콜이 있습니다 .

간단히 말해서 : 생성기는 게으르고 점증 적으로 보류중인 목록 이며 yield 문은 허용합니다 함수 표기법을 사용하여 생성기가 점진적으로 뱉어 져야하는 목록 값 을 프로그래밍해야합니다.

generator = myYieldingFunction(...)
x = list(generator)

   generator
       v
[x[0], ..., ???]

         generator
             v
[x[0], x[1], ..., ???]

               generator
                   v
[x[0], x[1], x[2], ..., ???]

                       StopIteration exception
[x[0], x[1], x[2]]     done

list==[x[0], x[1], x[2]]

파이썬의 makeRange과 같은 함수 range을 정의 해 봅시다. makeRange(n)에게 전화하면 발전기를 반환합니다.

def makeRange(n):
    # return 0,1,2,...,n-1
    i = 0
    while i < n:
        yield i
        i += 1

>>> makeRange(5)
<generator object makeRange at 0x19e4aa0>

생성기가 보류중인 값을 즉시 반환하도록하려면 반복 가능한 것처럼 list()에 전달할 수 있습니다.

>>> list(makeRange(5))
[0, 1, 2, 3, 4]

"목록 만 리턴"하는 예제 비교

위의 예는 단순히 추가하고 반환하는 목록을 만드는 것으로 생각할 수 있습니다.

# list-version                   #  # generator-version
def makeRange(n):                #  def makeRange(n):
    """return [0,1,2,...,n-1]""" #~     """return 0,1,2,...,n-1"""
    TO_RETURN = []               #>
    i = 0                        #      i = 0
    while i < n:                 #      while i < n:
        TO_RETURN += [i]         #~         yield i
        i += 1                   #          i += 1  ## indented
    return TO_RETURN             #>

>>> makeRange(5)
[0, 1, 2, 3, 4]

그러나 한 가지 중요한 차이점이 있습니다. 마지막 섹션을 참조하십시오.


생성기를 사용하는 방법

Iterable은 목록 이해의 마지막 부분이며 모든 생성기는 iterable이므로 종종 다음과 같이 사용됩니다.

#                   _ITERABLE_
>>> [x+10 for x in makeRange(5)]
[10, 11, 12, 13, 14]

발전기에 대해 더 나은 느낌을 얻으려면 itertools 모듈로 놀 수 있습니다 (보증시 chain 대신 chain.from_iterable를 사용해야합니다). 예를 들어, 생성기를 사용하여 itertools.count()과 같이 무한히 긴 지연 목록을 구현할 수도 있습니다. 자신 만의 def enumerate(iterable): Zip(count(), iterable)을 구현하거나 while 루프에서 yield 키워드를 사용하여 구현할 수도 있습니다.

참고 : 생성기는 실제로 coroutines 또는 비 결정적 프로그래밍 또는 기타 우아한 것들과 같은 더 많은 것들에 사용될 수 있습니다. 그러나 여기에 제시된 "게으른 목록"관점은 가장 일반적인 용도입니다.


무대 뒤에서

이것이 "Python iteration protocol"의 작동 방식입니다. 즉, list(makeRange(5))을 (를) 수행 할 때 진행되는 작업입니다. 이것이 내가 이전에 "게으른 증분 목록"으로 묘사 한 것입니다.

>>> x=iter(range(5))
>>> next(x)
0
>>> next(x)
1
>>> next(x)
2
>>> next(x)
3
>>> next(x)
4
>>> next(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

내장 함수 next()은 "반복 프로토콜"의 일부이며 모든 반복자에있는 .next() 함수를 호출합니다. next() 함수 (및 반복 프로토콜의 다른 부분)를 수동으로 사용하여 일반적으로 가독성을 희생시키면서 멋진 것을 구현할 수 있으므로 그렇게하지 마십시오 ...


사소한 점

일반적으로 대부분의 사람들은 다음과 같은 차이점에 신경 쓰지 않으며 여기에서 읽기를 중단하려고합니다.

Python-speak에서 iterable [1,2,3] 목록과 같은 "for-loop의 개념을 이해하는"객체이고 iterator [1,2,3].__iter__()과 같은 요청 된 for-loop의 특정 인스턴스입니다. generator 는 작성 방법 (함수 구문 포함)을 제외하고는 모든 iterator와 정확히 동일합니다.

목록에서 반복자를 요청하면 새 반복자가 작성됩니다. 그러나 반복자에게 반복자를 요청하면 (거의 수행하지는 않음) 자체 사본을 제공합니다.

따라서, 당신이 이런 일을하지 못하는 경우에는 ...

> x = myRange(5)
> list(x)
[0, 1, 2, 3, 4]
> list(x)
[]

... 제너레이터는 반복자 임을 기억하십시오. 즉, 일회용입니다. 다시 사용하려면 myRange(...)을 (를) 다시 호출해야합니다. 결과를 두 번 사용해야하는 경우 결과를 목록으로 변환하고 변수 x = list(myRange(5))에 저장하십시오. 생성기를 복제해야하는 사람 (예 : 끔찍한 해킹 메타 프로그래밍을 수행하는 사람)은 복사 가능한 반복자 Python이므로 절대적으로 필요한 경우 itertools.tee 를 사용할 수 있습니다. _ PEP 표준 제안이 연기되었습니다.

414
ninjagecko

파이썬에서 yield 키워드의 기능은 무엇입니까?

답변 개요/요약

  • yield 인 함수가 호출되면 Generator .을 반환합니다.
  • 생성기는 반복자 프로토콜 을 구현하므로 반복자이므로 반복자를 반복 할 수 있습니다.
  • 또한 생성기는 보낸 정보 일 수 있으며 개념적으로는 coroutine입니다.
  • Python 3에서 yield from을 (를) 사용하여 한 발전기에서 다른 발전기로 delegate 할 수 있습니다.
  • (부록은 상위 답변을 포함하여 두 가지 답변을 비판하고 생성기에서 return 사용에 대해 설명합니다.)

발전기 :

yield은 함수 정의 내에서만 유효하며 함수 정의에 yield을 포함하면 생성기를 반환합니다.

생성기의 아이디어는 다양한 구현으로 다른 언어 (각주 1 참조)에서 나옵니다. 파이썬 제너레이터에서 코드 실행은 수율 시점에서 frozen 입니다. 생성기가 호출되면 (방법에 대해서는 아래에서 설명) 실행이 재개 된 후 다음 수율로 정지됩니다.

yield은 ___iter___ 및 next (Python 2) 또는 ___next___ (Python 3)의 두 가지 방법으로 정의 된 반복기 프로토콜 구현 의 쉬운 방법을 제공합니다. 이 두 가지 방법 모두 Iterator 모듈에서 collections 추상 기본 클래스를 사용하여 유형을 확인할 수있는 객체를 반복자로 만듭니다.

_>>> def func():
...     yield 'I am'
...     yield 'a generator!'
... 
>>> type(func)                 # A function with yield is still a function
<type 'function'>
>>> gen = func()
>>> type(gen)                  # but it returns a generator
<type 'generator'>
>>> hasattr(gen, '__iter__')   # that's an iterable
True
>>> hasattr(gen, 'next')       # and with .next (.__next__ in Python 3)
True                           # implements the iterator protocol.
_

생성기 유형은 반복기의 하위 유형입니다.

_>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True
_

필요한 경우 다음과 같이 유형을 확인할 수 있습니다.

_>>> isinstance(gen, types.GeneratorType)
True
>>> isinstance(gen, collections.Iterator)
True
_

Iterator한 번 소진 된 기능 의 기능은 재사용하거나 재설정 할 수 없다는 것입니다.

_>>> list(gen)
['I am', 'a generator!']
>>> list(gen)
[]
_

기능을 다시 사용하려면 다른 것을 만들어야합니다 (각주 2 참조).

_>>> list(func())
['I am', 'a generator!']
_

다음과 같이 프로그래밍 방식으로 데이터를 생성 할 수 있습니다.

_def func(an_iterable):
    for item in an_iterable:
        yield item
_

위의 간단한 생성기는 다음과 같습니다. Python 3.3 (Python 2에서는 사용할 수 없음)에서 yield from 을 사용할 수 있습니다.

_def func(an_iterable):
    yield from an_iterable
_

그러나 _yield from_는 하위 생성자에 대한 위임도 허용합니다. 하위 생성자와의 공동 위임에 대해서는 다음 섹션에서 설명합니다.

코 루틴 :

yield은 데이터를 생성기로 보낼 수있는 표현식을 형성합니다 (각주 3 참조)

다음은 예입니다. received 변수에 유의하십시오.이 변수는 생성기로 전송되는 데이터를 가리 킵니다.

_def bank_account(deposited, interest_rate):
    while True:
        calculated_interest = interest_rate * deposited 
        received = yield calculated_interest
        if received:
            deposited += received


>>> my_account = bank_account(1000, .05)
_

먼저 내장 함수 next 으로 생성기를 큐잉해야합니다. 사용중인 Python 버전에 따라 적절한 next 또는 ___next___ 메소드를 호출합니다.

_>>> first_year_interest = next(my_account)
>>> first_year_interest
50.0
_

이제 생성기로 데이터를 보낼 수 있습니다. ( None 전송은 next 호출과 동일합니다.) :

_>>> next_year_interest = my_account.send(first_year_interest + 1000)
>>> next_year_interest
102.5
_

_yield from_를 사용하여 서브-코 루틴에 협력 위임

이제 _yield from_을 Python 3에서 사용할 수 있습니다.이를 통해 코 루틴을 하위 코 루틴에 위임 할 수 있습니다.

_def money_manager(expected_rate):
    under_management = yield     # must receive deposited value
    while True:
        try:
            additional_investment = yield expected_rate * under_management 
            if additional_investment:
                under_management += additional_investment
        except GeneratorExit:
            '''TODO: write function to send unclaimed funds to state'''
        finally:
            '''TODO: write function to mail tax info to client'''


def investment_account(deposited, manager):
    '''very simple model of an investment account that delegates to a manager'''
    next(manager) # must queue up manager
    manager.send(deposited)
    while True:
        try:
            yield from manager
        except GeneratorExit:
            return manager.close()
_

이제 하위 생성기에 기능을 위임 할 수 있으며 위와 같이 생성기에서 사용할 수 있습니다.

_>>> my_manager = money_manager(.06)
>>> my_account = investment_account(1000, my_manager)
>>> first_year_return = next(my_account)
>>> first_year_return
60.0
>>> next_year_return = my_account.send(first_year_return + 1000)
>>> next_year_return
123.6
_

PEP 380]에서 _yield from_의 정확한 의미에 대해 자세히 읽을 수 있습니다.

다른 방법 : 닫기 및 던지기

close 메서드는 함수 실행이 고정 된 시점에서 GeneratorExit을 발생시킵니다. ___del___에 의해 호출되므로 GeneratorExit을 처리하는 곳에 정리 코드를 넣을 수 있습니다.

_>>> my_account.close()
_

생성기에서 처리하거나 사용자에게 다시 전파 할 수있는 예외를 throw 할 수도 있습니다.

_>>> import sys
>>> try:
...     raise ValueError
... except:
...     my_manager.throw(*sys.exc_info())
... 
Traceback (most recent call last):
  File "<stdin>", line 4, in <module>
  File "<stdin>", line 2, in <module>
ValueError
_

결론

다음 질문의 모든 측면을 다루었다고 생각합니다.

파이썬에서 yield 키워드의 기능은 무엇입니까?

yield이 많은 일을하는 것으로 나타났습니다. 나는 이것에 더 철저한 예를 추가 할 수 있다고 확신합니다. 더 많은 것을 원하거나 건설적인 비판이 필요한 경우 아래에 의견을 보내 알려주십시오.


충수:

최고/허용 된 답변의 비판 **

  • 예를 들어 목록을 사용하여 iterable을 만드는 것에 혼란이 있습니다. 위의 내 참조를 참조하십시오. 그러나 요약에는 iterable에 iterator을 반환하는 ___iter___ 메서드가 있습니다. iterator은 _.next_ (Python 2 또는 _.__next___ (Python 3) 메소드를 제공합니다.이 메소드는 for을 발생시킬 때까지 StopIteration 루프에 의해 암시 적으로 호출되며, 일단 수행되면 계속됩니다. 그렇게하기 위해.
  • 그런 다음 생성기 표현식을 사용하여 생성기가 무엇인지 설명합니다. 제너레이터는 단순히 iterator를 생성하는 편리한 방법이므로 문제를 혼동하기 때문에 아직 yield 부분에 도달하지 못했습니다.
  • 생성기 소진 제어에서 _.next_ 메소드를 호출하고 대신 내장 함수 next을 사용해야합니다. 그의 코드는 Python 3에서 작동하지 않기 때문에 적절한 간접 계층이 될 것입니다.
  • Itertools? 이것은 yield의 기능과 전혀 관련이 없습니다.
  • yield이 Python의 새로운 기능 _yield from_과 함께 제공하는 메서드에 대한 설명은 없습니다. 3. 최상위/허용 된 답변은 매우 불완전한 답변입니다.

생성기 표현 또는 이해에서 yield을 제안하는 대답에 대한 비판.

문법은 현재 목록 이해의 표현을 허용합니다.

_expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
                     ('=' (yield_expr|testlist_star_expr))*)
...
yield_expr: 'yield' [yield_arg]
yield_arg: 'from' test | testlist
_

수율은 표현이기 때문에, 특히 유스 케이스를 잘 언급하지는 않았지만 이해력이나 생성기 표현에 사용하는 것이 흥미로 웠습니다.

CPython 핵심 개발자는 허용을 거부하는 논의 입니다. 메일 링리스트의 관련 게시물은 다음과 같습니다.

Brett Cannon은 2017 년 1 월 30 일 19:05에 다음과 같이 썼습니다.

2017 년 1 월 29 일 일요일 16:39에 Craig Rodrigues는 다음과 같이 썼습니다.

어느 쪽이든 접근해도 괜찮습니다. IMHO는 Python 3에있는 그대로 두는 것이 좋지 않습니다.

내 투표는 구문에서 기대하는 것을 얻지 못하기 때문에 SyntaxError입니다.

현재 행동에 의존하는 코드가 실제로 유지하기에는 너무 영리하기 때문에 우리가 끝내기에 합리적인 장소라는 데 동의합니다.

도착하는 관점에서 우리는 다음을 원할 것입니다.

  • 3.7의 구문 경고 또는 비추천 경고
  • 2.7.x의 Py3k 경고
  • 3.8의 구문 오류

건배, 닉.

-닉 코 글란 | gmail.com에서 ncoghlan | 브리즈번, 오스트레일리아

더구나, outstanding issue (10544) 이 방향을 가리키고있는 것 같습니다. 절대 좋은 생각이 아닙니다 (PyPy, Python Python으로 작성된 구현은 이미 구문 경고를 발생시킵니다.)

결론은 CPython 개발자가 달리 말할 때까지 : yield을 생성기 식이나 이해에 넣지 마십시오.

생성기의 return

Python 2 에서 :

생성기 함수에서 return 문은 _expression_list_을 포함 할 수 없습니다. 이 컨텍스트에서 베어 return은 생성기가 완료되었으며 StopIteration이 발생 함을 나타냅니다.

_expression_list_는 기본적으로 쉼표로 구분 된 여러 표현식입니다. 기본적으로 Python 2에서는 return로 생성기를 중지 할 수 있지만 값을 반환 할 수는 없습니다.

Python 에서 :

생성기 함수에서 return 문은 생성기가 완료되었음을 나타내며 StopIteration이 발생하게합니다. 리턴 된 값 (있는 경우)은 StopIteration을 구성하는 인수로 사용되며 _StopIteration.value_ 속성이됩니다.

각주

  1. CLU, Sather 및 Icon 언어는 제안서에서 생성기 개념을 Python에 도입하기 위해 참조되었습니다. 일반적인 아이디어는 함수가 내부 상태를 유지하고 사용자의 요청에 따라 중간 데이터 포인트를 생성 할 수 있다는 것입니다. 이것은 Python threading 을 포함한 다른 접근 방식에 비해 성능이 뛰어나며 일부 시스템에서는 사용할 수 없습니다.

  2. 예를 들어 xrange 객체 (Python 3의 range)는 재사용 할 수 있기 때문에 반복 가능하더라도 Iterators가 아닙니다. 목록과 마찬가지로 ___iter___ 메서드는 반복자 객체를 반환합니다.

  3. yield은 원래 명령문으로 도입되었으므로 코드 블록의 줄 시작 부분에만 나타날 수 있습니다. 이제 yield은 yield 표현식을 만듭니다. https://docs.python.org/2/reference/simple_stmts.html#grammar-token-yield_stmt 이 변경은 proposed 사용자가 데이터를 하나는 그것을받을 수도 있습니다. 데이터를 보내려면 데이터를 무언가에 할당 할 수 있어야하며, 그 때문에 명세서는 작동하지 않습니다.

331
Aaron Hall

yieldreturn과 비슷합니다 - 여러분이 말한 것을 (생성기로) 반환합니다. 차이점은 다음에 생성기를 호출 할 때 마지막 호출에서 yield 문 실행이 시작된다는 것입니다. return과 달리 yield가 발생할 때 스택 프레임은 정리되지 않지만 컨트롤은 호출자에게 다시 전송되므로 함수가 호출 될 때 해당 상태가 다시 시작됩니다.

코드의 경우 get_child_candidates 함수는 반복자처럼 작동하므로 목록을 확장하면 한 번에 하나의 요소 만 새 목록에 추가됩니다.

list.extend는 반복자가 다 소진 될 때까지 호출합니다. 게시 한 코드 샘플의 경우, 튜플을 반환하고 목록에 추가하는 것이 훨씬 더 명확합니다.

260
Douglas Mayle

언급해야 할 점이 한 가지 더 있습니다. 결과가 실제로 종료되지 않아도되는 함수입니다. 다음과 같은 코드를 작성했습니다.

def fib():
    last, cur = 0, 1
    while True: 
        yield cur
        last, cur = cur, last + cur

그런 다음이 코드를 다른 코드에서 사용할 수 있습니다.

for f in fib():
    if some_condition: break
    coolfuncs(f);

정말 일부 문제를 단순화하는 데 도움이되며 작업하기가 더 쉽습니다. 

199
Claudiu

TL; DR

이 대신에 :

def square_list(n):
    the_list = []                         # Replace
    for x in range(n):
        y = x * x
        the_list.append(y)                # these
    return the_list                       # lines

이 작업을 수행:

def square_yield(n):
    for x in range(n):
        y = x * x
        yield y                           # with this one.

처음부터 목록을 작성하는 것을 발견 할 때마다 yield 각 조각을 대신 사용하십시오.

이것은 수확량을 가진 나의 첫번째 "aha"순간이었다.


yield은 (는) 설탕 말하는 방법입니다

일련의 물건을 만들다

같은 행동 :

>>> for square in square_list(4):
...     print(square)
...
0
1
4
9
>>> for square in square_yield(4):
...     print(square)
...
0
1
4
9

다른 행동 :

수율은 단일 패스 입니다. 한 번만 반복 할 수 있습니다. 함수에 수율이 있으면 generator function 이라고합니다. 그리고 iterator 가 반환합니다. 그 용어가 드러납니다. 우리는 컨테이너의 편의성을 잃지 만 필요에 따라 계산되고 임의로 긴 시리즈의 성능을 얻습니다.

수율은 게으른 이며 계산을 중단합니다. 수율이있는 함수 는 실제로 호출 할 때 전혀 실행되지 않습니다. 반복자 객체 를 반환합니다. 반복자에서 next()을 호출 할 때마다 (이것은 for-loop에서 실행) 다음 수율로 앞뒤로 움직입니다. return은 StopIteration을 발생시키고 시리즈를 종료합니다 (이것은 for-loop의 자연스러운 끝입니다).

수율은 다목적 입니다. 데이터를 모두 함께 저장할 필요는 없으며 한 번에 하나씩 사용할 수 있습니다. 무한 할 수 있습니다.

>>> def squares_all_of_them():
...     x = 0
...     while True:
...         yield x * x
...         x += 1
...
>>> squares = squares_all_of_them()
>>> for _ in range(4):
...     print(next(squares))
...
0
1
4
9

다중 패스 가 필요하고 시리즈가 너무 길지 않은 경우 list()을 호출하십시오.

>>> list(square_yield(4))
[0, 1, 4, 9]

두 의미 가 적용되기 때문에 yield이라는 단어를 훌륭하게 선택하십시오.

수확량 — 생산 또는 제공 (농업에서와 같이)

... 시리즈의 다음 데이터를 제공하십시오.

수익률 — 길을 포기하거나 포기합니다 (정치 권력에서와 같이)

반복자가 진행될 때까지 CPU 실행을 포기합니다.

184
Bob Stein

최소한의 작업 예제를 선호하는 사람들은이 대화 형 Python 세션을 명상하십시오.

>>> def f():
...   yield 1
...   yield 2
...   yield 3
... 
>>> g = f()
>>> for i in g:
...   print i
... 
1
2
3
>>> for i in g:
...   print i
... 
>>> # Note that this time nothing was printed
171
Daniel

발전기를 반환합니다. 나는 파이썬에 특히 익숙하지 않지만, 그것이 익숙하다면 C #의 반복자 블록 과 같은 종류라고 생각합니다.

핵심 아이디어는 호출자에 관한 한 컴파일러/인터프리터/트릭을 수행하여 next ()를 계속 호출 할 수 있고 계속 다음과 같이 값을 반환한다는 것입니다- 생성기 메서드가 일시 중지되었습니다 . 이제는 실제로 메소드를 "일시 정지"할 수 없으므로 컴파일러는 현재 위치와 로컬 변수 등이 무엇인지 기억하기 위해 상태 머신을 빌드합니다. 이터레이터를 직접 작성하는 것보다 훨씬 쉽습니다.

161
Jon Skeet

생성기를 사용하는 방법을 설명하는 많은 훌륭한 답변 중에서 아직 느끼지 못한 한 가지 유형의 답변이 있습니다. 프로그래밍 언어 이론 답변은 다음과 같습니다.

Python의 yield 문은 생성기를 반환합니다. Python의 생성기는 다음을 반환하는 함수입니다. 연속 (특히 코 루틴 유형이지만 연속은 진행 상황을 이해하는 더 일반적인 메커니즘을 나타냅니다).

프로그래밍 언어 이론의 연속은 훨씬 더 기본적인 종류의 계산이지만 추론하기가 매우 어렵고 구현하기가 어렵 기 때문에 자주 사용되지는 않습니다. 그러나 연속이 무엇인지에 대한 아이디어는 간단합니다. 아직 완료되지 않은 계산 상태입니다. 이 상태에서 변수의 현재 값, 아직 수행하지 않은 작업 등이 저장됩니다. 그런 다음 프로그램의 어느 시점에서 프로그램 변수가 해당 상태로 재설정되고 저장된 작업이 수행되도록 연속을 호출 할 수 있습니다.

보다 일반적인 형태의 연속은 두 가지 방식으로 구현 될 수 있습니다. call/cc 방식으로, 프로그램의 스택은 문자 그대로 저장되고 연속이 호출되면 스택이 복원됩니다.

연속 전달 스타일 (CPS)에서 연속은 프로그래머가 명시 적으로 관리하고 서브 루틴으로 전달하는 일반 함수 (함수가 클래스 인 언어에서만)입니다. 이 스타일에서 프로그램 상태는 스택의 어딘가에있는 변수가 아니라 클로저 (및 그 안에 인코딩되는 변수)로 표시됩니다. 제어 흐름을 관리하는 함수는 연속을 인수로 받아들이고 (CPS의 일부 변형에서는 함수가 여러 개의 연속을 허용 할 수 있음) 단순히 호출 한 후 호출하여 제어 흐름을 호출하여 제어 흐름을 조작합니다. 연속 전달 스타일의 매우 간단한 예는 다음과 같습니다.

def save_file(filename):
  def write_file_continuation():
    write_stuff_to_file(filename)

  check_if_file_exists_and_user_wants_to_overwrite(write_file_continuation)

이 (매우 간단한) 예에서, 프로그래머는 실제로 파일을 연속으로 작성하는 작업을 저장하고 (이는 잠재적으로 많은 세부 사항이있는 매우 복잡한 작업 일 수 있음) 해당 연속을 전달합니다 (예 : 첫 번째 더 많은 처리를 수행하고 필요한 경우 호출하는 다른 연산자에게 클래스 폐쇄). (이 디자인 패턴은 실제 GUI 프로그래밍에서 많이 사용됩니다. 왜냐하면 코드가 절약되거나 GUI 이벤트 트리거 후 제어 흐름을 관리하기 때문입니다.)

이 게시물의 나머지 부분은 일반성을 잃지 않고 CPS로 연속성을 개념화합니다. 이해하기 쉽고 읽기가 쉽지 않기 때문입니다.


이제 파이썬에서 생성기에 대해 이야기 해 봅시다. 생성기는 연속의 특정 하위 유형입니다. 반면 연속은 일반적으로 계산 상태를 저장할 수 있습니다 (예 : 프로그램의 호출 스택 ), 생성자는 반복자 를 통해서만 반복 상태를 저장할 수 있습니다. 그러나이 정의는 생성기의 특정 사용 사례에서 약간 오해의 소지가 있습니다. 예를 들어 :

def f():
  while True:
    yield 4

이것은 동작이 잘 정의 된 합리적인 반복 가능한 것입니다. 제너레이터가 반복 할 때마다 4를 반환합니다 (그리고 영원히). 그러나 반복자를 생각할 때 떠오를 수있는 프로토 타입 유형의 반복 가능한 것은 아닙니다 (예 : for x in collection: do_something(x)). 이 예제는 생성기의 힘을 보여줍니다. 반복자가 있으면 생성기가 반복 상태를 저장할 수 있습니다.

반복 : 연속은 프로그램의 스택 상태를 저장할 수 있으며 생성기는 반복 상태를 저장할 수 있습니다. 이는 연속성이 생성기보다 훨씬 강력하지만 생성기가 훨씬 훨씬 쉽다는 것을 의미합니다. 언어 디자이너가 구현하기가 더 쉽고 프로그래머가 사용하기가 더 쉽습니다 (구울 시간이 있으면 이 페이지는 연속 및 call/cc 에 대해 이해하십시오).

그러나 간단하고 구체적인 연속 전달 스타일로 생성기를 쉽게 구현하고 개념화 할 수 있습니다.

yield이 호출 될 때마다 함수가 연속을 반환하도록 지시합니다. 함수가 다시 호출되면 중단 된 위치부터 시작합니다. 따라서 의사 의사 코드 (의사 코드가 아니라 코드가 아닌)에서 생성기의 next 메소드는 기본적으로 다음과 같습니다.

class Generator():
  def __init__(self,iterable,generatorfun):
    self.next_continuation = lambda:generatorfun(iterable)

  def next(self):
    value, next_continuation = self.next_continuation()
    self.next_continuation = next_continuation
    return value

여기서 yield 키워드는 실제로 실제 생성기 함수의 구문 설탕이며 기본적으로 다음과 같습니다.

def generatorfun(iterable):
  if len(iterable) == 0:
    raise StopIteration
  else:
    return (iterable[0], lambda:generatorfun(iterable[1:]))

이것은 의사 코드 일 뿐이며 Python의 실제 생성기 구현은 더 복잡합니다. 그러나 진행 상황을 이해하기위한 연습으로 yield 키워드를 사용하지 않고 연속 전달 스타일을 사용하여 생성기 객체를 구현해보십시오.

149
aestrivex

수율은 발전기를 제공합니다. 

def get_odd_numbers(i):
    return range(1, i, 2)
def yield_odd_numbers(i):
    for x in range(1, i, 2):
       yield x
foo = get_odd_numbers(10)
bar = yield_odd_numbers(10)
foo
[1, 3, 5, 7, 9]
bar
<generator object yield_odd_numbers at 0x1029c6f50>
bar.next()
1
bar.next()
3
bar.next()
5

보시다시피, 첫 번째 경우 foo는 전체 목록을 메모리에 한 번에 보유합니다. 5 가지 요소가있는 목록에는 큰 문제가 아니지만 5 백만 가지 목록을 원한다면 어떻게 될까요? 이것은 거대한 메모리 먹는 사람 일뿐만 아니라 함수가 호출 될 때 빌드하는 데 많은 시간을 소모합니다. 두 번째 경우에는 막대가 생성기를 제공합니다. 생성자는 iterable입니다. 즉, for 루프 등에서 사용할 수 있지만 각 값은 한 번만 액세스 할 수 있습니다. 모든 값은 동시에 메모리에 저장되지 않습니다. 생성자 객체는 마지막으로 호출 할 때 루핑 된 위치를 "기억"합니다. 이렇게하면 반복 가능한 반복을 사용하여 500 억까지 계산하면 500 억 개까지 계산할 필요가 없습니다. 즉시 계산할 500 억 개의 숫자를 저장합니다. 다시 말하지만 이것은 상당히 고안된 예입니다. 실제로 500 억에 달하는 것을 원한다면 itertools를 사용할 것입니다. :)

이것은 발전기의 가장 간단한 사용 사례입니다. 앞에서 말했듯이 yield를 사용하여 스택 변수를 사용하는 대신 호출 스택을 통해 물건을 밀어 올리면 효율적 순열을 작성할 수 있습니다. 생성기는 특수 트리 탐색 및 기타 모든 방법에 사용될 수도 있습니다.

148
RBansal

다음은 일반 언어로 된 예입니다. 나는 높은 수준의 인간 개념과 낮은 수준의 파이썬 개념 사이의 통신을 제공 할 것이다.

일련의 숫자에 대해 작업하고 싶지만 그 시퀀스의 생성으로 내 자신을 괴롭히기를 원하지 않습니다. 원하는 작업에만 집중하고 싶습니다. 그래서, 나는 다음과 같이한다.

  • 나는 당신을 부르며 특정한 방식으로 생산되는 일련의 수를 원한다는 것을 말하고 알고리즘이 무엇인지 알려주겠습니다. 
    이 단계는 생성기 함수, 즉 def을 포함하는 함수를 yieldining하는 것과 같습니다.
  • 언젠가 나중에, 나는 당신에게 "좋아, 숫자의 순서를 말해 줄 준비를해라."라고 말한다. 
    이 단계는 생성자 객체를 반환하는 생성자 함수를 호출하는 것과 같습니다. 아직 어떤 번호도 알려주지 않았다는 점에 유의하십시오. 당신은 당신의 종이와 연필을 움켜 잡습니다.
  • 나는 너에게 "다음 번호를 말해줘."라고 물어 보면 첫 번째 번호를 말해 준다. 그 후에, 다음 전화 번호를 물어보기를 기다립니다. 당신이 어디에 있었는지, 이미 말한 번호와 다음 번호가 무엇인지 기억하는 것이 당신의 일입니다. 나는 세부 사항에 관심이 없다. 
    이 단계는 생성자 객체에서 .next()을 호출하는 것과 같습니다.
  • ... 이전 단계를 반복 할 때까지 ...
  • 결국, 당신은 끝날 수도 있습니다. 너는 나에게 숫자를 말하지 않는다. 너는 단지 소리 지른다. "너의 말을 잡아라. 다 끝났어! 더 이상 숫자는 없어!" 
    이 단계는 해당 작업을 종료하는 생성기 개체에 해당하며 StopIteration 예외를 발생시킵니다. generator 함수는 예외를 발생시킬 필요가 없습니다. 함수가 종료되거나 return을 발행하면 자동으로 발생합니다.

이것은 생성기가하는 일 (yield을 포함하는 함수)입니다. 실행을 시작하고 yield을 수행 할 때마다 일시 중지하며 .next() 값을 요청하면 마지막 시점부터 계속됩니다. 순차적으로 값을 요청하는 방법을 설명하는 Python의 반복자 프로토콜을 완벽하게 지원합니다.

반복자 프로토콜의 가장 유명한 사용자는 Python에서 for 명령입니다. 그래서, 당신이 할 때마다 :

for item in sequence:

sequence이리스트, 문자열, 사전 또는 generator object like 인 경우에는 문제가되지 않습니다. 결과는 동일합니다. 즉, 항목을 순차적으로 읽습니다.

def 키워드가 포함 된 함수를 yieldining하는 것은 생성기를 만드는 유일한 방법이 아닙니다. 그것은 단지 하나를 만드는 가장 쉬운 방법입니다.

보다 정확한 정보를 얻으려면 Python 문서에서 iterator types , yield statementgenerators 에 대해 읽어보십시오.

120
tzot

많은 답변이 yield을 사용하여 생성기를 만드는 이유를 보여 주지만 yield을 더 많이 사용합니다. 코 루틴을 만드는 것은 매우 쉽습니다. 두 개의 코드 블록간에 정보를 전달할 수 있습니다. 생성기를 생성하기 위해 yield을 사용하는 것에 대해 이미 주어진 훌륭한 예제를 반복하지 않을 것입니다.

다음 코드에서 yield이하는 일을 이해하려면 yield이있는 코드를 통해주기를 추적 할 수 있습니다. 손가락이 yield에 닿을 때마다 next 또는 send이 입력 될 때까지 기다려야합니다. next이 호출되면 yield에 도달 할 때까지 코드를 추적합니다 ... yield 오른쪽의 코드가 평가되고 호출자에게 반환됩니다. 그러면 기다립니다. next이 다시 호출되면 코드를 통해 다른 루프를 수행합니다. 그러나 코 루틴에서는 yieldsend과 함께 사용할 수 있습니다 ... 이는 호출자 into yielding 함수에서 값을 보냅니다. send이 주어지면 yield은 전송 된 값을 받아서 왼쪽으로 뱉어냅니다. 그러면 yield을 다시 히트 할 때까지 (코드가 next이 호출 된 것처럼 값을 반환 할 때까지) 코드를 따라가는 추적이 진행됩니다.

예 :

>>> def coroutine():
...     i = -1
...     while True:
...         i += 1
...         val = (yield i)
...         print("Received %s" % val)
...
>>> sequence = coroutine()
>>> sequence.next()
0
>>> sequence.next()
Received None
1
>>> sequence.send('hello')
Received hello
2
>>> sequence.close()
105
Mike McKerns

yield이라는 또 다른 사용법과 의미가 있습니다 (Python 3.3 이후) :

yield from <expr>

PEP 380 - 하위 생성자로 위임하기위한 구문:

생성기가 다른 생성기로 조작의 일부를 위임하는 구문이 제안됩니다. 이렇게하면 'yield'를 포함하는 코드 섹션을 계산하여 다른 생성기에 배치 할 수 있습니다. 또한 하위 생성자는 값을 반환 할 수 있으며 값은 위임 생성자에서 사용할 수 있습니다.

새로운 구문은 또한 하나의 생성기가 다른 생성기에 의해 생성 된 값을 다시 산출 할 때 최적화를위한 몇 가지 기회를 열어줍니다.

더욱이 this 는 (파이썬 3.5부터) 소개 할 것이다.

async def new_coroutine(data):
   ...
   await blocking_action()

코 루틴이 일반 생성기와 혼동되지 않도록 (오늘날 yield이 둘 다 사용됩니다.).

97
Sławomir Lenart

모든 중대한 답변, 그러나 초보자에게는 조금 어려움.

나는 당신이 return 문장을 배웠다고 가정합니다.

비유로 returnyield은 쌍둥이입니다. return은 'return and stop'을 의미하지만 'yield'는 'return, but continue'를 의미합니다.

  1. return을 사용하여 num_list를 가져 오십시오.
def num_list(n):
    for i in range(n):
        return i

그것을 실행하십시오 :

In [5]: num_list(3)
Out[5]: 0

보시다시피, 당신은 하나의 번호 만 얻습니다. return을 사용하면 행복하게 이기고 결코 한 번만 구현하고 종료 할 수 없습니다.

  1. yield이 있습니다.

returnyield로 바꿉니다.

In [10]: def num_list(n):
    ...:     for i in range(n):
    ...:         yield i
    ...:

In [11]: num_list(3)
Out[11]: <generator object num_list at 0x10327c990>

In [12]: list(num_list(3))
Out[12]: [0, 1, 2]

자, 당신은 모든 숫자를 얻기 위해 승리합니다.

한 번 실행되고 중지되는 return과 비교하여 yield은 계획 한 시간을 실행합니다. returnreturn one of them로, yieldreturn all of them로 해석 할 수 있습니다. 이것을 iterable이라고합니다.

  1. 우리가 yield 문을 return로 다시 작성할 수있는 한 걸음 더 나아갑니다.
In [15]: def num_list(n):
    ...:     result = []
    ...:     for i in range(n):
    ...:         result.append(i)
    ...:     return result

In [16]: num_list(3)
Out[16]: [0, 1, 2]

그것은 yield에 관한 핵심입니다.

목록 return 출력과 개체 yield 출력의 차이점은 다음과 같습니다.

당신은 항상리스트 오브젝트로부터 [0, 1, 2]를 얻을 것이지만, '오브젝트 yield output'에서 한 번만 검색 할 수 있습니다. 따라서 Out[11]: <generator object num_list at 0x10327c990>에 표시된대로 새 이름 generator 개체가 있습니다.

결론적으로, 그것을 비유하는 은유로서 :

  • returnyield은 쌍둥이입니다.
  • listgenerator은 쌍둥이입니다.
86
JawSaw

파이썬이 실제로 생성자를 구현하는 방법에 대한 파이썬 예제는 다음과 같습니다.

파이썬 생성기로서 :

from itertools import islice

def fib_gen():
    a, b = 1, 1
    while True:
        yield a
        a, b = b, a + b

assert [1, 1, 2, 3, 5] == list(islice(fib_gen(), 5))

생성자 대신 렉시 컬 클로저 사용

def ftake(fnext, last):
    return [fnext() for _ in xrange(last)]

def fib_gen2():
    #funky scope due to python2.x workaround
    #for python 3.x use nonlocal
    def _():
        _.a, _.b = _.b, _.a + _.b
        return _.a
    _.a, _.b = 0, 1
    return _

assert [1,1,2,3,5] == ftake(fib_gen2(), 5)

생성자 대신 객체 클로저 사용 (왜냐하면 ClosuresAndObjectsAreEquivalent )

class fib_gen3:
    def __init__(self):
        self.a, self.b = 1, 1

    def __call__(self):
        r = self.a
        self.a, self.b = self.b, self.a + self.b
        return r

assert [1,1,2,3,5] == ftake(fib_gen3(), 5)
83
Dustin Getz

저는 "발전기에 대한 간단한 설명은 Beazley의 'Python : Essential Reference'페이지 19를 읽었습니다.하지만 많은 사람들이 이미 좋은 설명을 올렸습니다.

또한 yield은 생성자 함수에서 이중 용도로 coroutines에서 사용할 수 있습니다. 코드 스 니펫과 동일한 용도는 아니지만 (yield)는 함수의 표현식으로 사용할 수 있습니다. 호출자가 send() 메소드를 사용하여 메소드에 값을 전송하면 coroutine은 다음 (yield) 문이 발생할 때까지 실행됩니다.

생성기와 동시 루틴은 데이터 흐름 유형 응용 프로그램을 설정하는 멋진 방법입니다. 함수에서 yield 문을 다른 용도로 사용하는 것이 가치가 있다고 생각했습니다.

81
johnzachary

프로그래밍 관점에서, 이터레이터는 thunks 로 구현됩니다.

동시 실행 등을위한 반복자, 생성자 및 스레드 풀을 썽크 (익명 함수라고도 함)로 구현하기 위해 디스패처가있는 클로저 객체로 전송 된 메시지를 사용하고 디스패처는 "메시지"에 응답합니다.

http://en.wikipedia.org/wiki/Message_passing

" next "는 " iter "호출에 의해 생성 된 클로저에 전송 된 메시지입니다.

이 계산을 구현하는 데는 여러 가지 방법이 있습니다. 나는 돌연변이를 사용했으나 현재 값과 다음 야수를 돌려줌으로써 돌연변이없이 그것을하는 것은 쉽습니다.

다음은 R6RS의 구조를 사용하는 데모이지만, 의미는 파이썬과 완전히 동일합니다. 그것은 동일한 계산 모델이며 파이썬에서 구문을 변경하기 위해서만 변경해야합니다.

Welcome to Racket v6.5.0.3.

-> (define gen
     (lambda (l)
       (define yield
         (lambda ()
           (if (null? l)
               'END
               (let ((v (car l)))
                 (set! l (cdr l))
                 v))))
       (lambda(m)
         (case m
           ('yield (yield))
           ('init  (lambda (data)
                     (set! l data)
                     'OK))))))
-> (define stream (gen '(1 2 3)))
-> (stream 'yield)
1
-> (stream 'yield)
2
-> (stream 'yield)
3
-> (stream 'yield)
'END
-> ((stream 'init) '(a b))
'OK
-> (stream 'yield)
'a
-> (stream 'yield)
'b
-> (stream 'yield)
'END
-> (stream 'yield)
'END
->
77
alinsoar

다음은 간단한 예입니다.

def isPrimeNumber(n):
    print "isPrimeNumber({}) call".format(n)
    if n==1:
        return False
    for x in range(2,n):
        if n % x == 0:
            return False
    return True

def primes (n=1):
    while(True):
        print "loop step ---------------- {}".format(n)
        if isPrimeNumber(n): yield n
        n += 1

for n in primes():
    if n> 10:break
    print "wiriting result {}".format(n)

산출:

loop step ---------------- 1
isPrimeNumber(1) call
loop step ---------------- 2
isPrimeNumber(2) call
loop step ---------------- 3
isPrimeNumber(3) call
wiriting result 3
loop step ---------------- 4
isPrimeNumber(4) call
loop step ---------------- 5
isPrimeNumber(5) call
wiriting result 5
loop step ---------------- 6
isPrimeNumber(6) call
loop step ---------------- 7
isPrimeNumber(7) call
wiriting result 7
loop step ---------------- 8
isPrimeNumber(8) call
loop step ---------------- 9
isPrimeNumber(9) call
loop step ---------------- 10
isPrimeNumber(10) call
loop step ---------------- 11
isPrimeNumber(11) call

저는 파이썬 개발자는 아니지만, yield은 프로그램 흐름의 위치를 ​​유지하고 "yield"위치에서 다음 루프를 시작합니다. 그것은 그 위치에서 기다리고있는 것처럼 보입니다. 그리고 그 직전에 값을 외부로 반환하고 다음에 계속 작동합니다.

그것은 흥미 있고 멋진 능력 인 것처럼 보인다 : D

70
Engin OZTURK

다음은 yield이하는 일에 대한 정신적 인 이미지입니다.

쓰레드가 스택을 가지고 있다고 생각하는 것은 (비록 그렇게 구현되지 않았을지라도).

정상 함수가 호출되면 로컬 변수를 스택에 넣고 계산을 한 다음 스택을 지우고 반환합니다. 그 지역 변수의 값은 결코 다시는 보이지 않습니다.

yield 함수를 사용하면 코드가 실행될 때 (즉, 함수가 호출 된 후 next() 메소드가 호출되는 생성기 객체를 반환 한 후) 로컬 변수를 스택에 넣고 잠시 동안 계산합니다. 그런 다음 yield 문을 쳤을 때 스택의 일부를 지우고 돌아 오기 전에 로컬 변수의 스냅 샷을 생성자 객체에 저장합니다. 또한 코드의 현재 위치 (특정 yield 문)를 기록합니다.

따라서 발전기가 걸려있는 고정 된 기능입니다.

이후에 next()이 호출되면 함수의 소지품을 스택으로 가져 와서 다시 애니메이션화합니다. 이 함수는 중단 된 지점부터 계산을 계속하면서 추운 곳에서 영원을 보냈다는 사실을 잊어 버립니다.

다음 예를 비교하십시오.

def normalFunction():
    return
    if False:
        pass

def yielderFunction():
    return
    if False:
        yield 12

두 번째 함수를 호출하면 첫 번째 함수와 매우 다르게 동작합니다. yield 문은 도달 할 수 없지만, 그것이 존재한다면 그것은 우리가 다루고있는 것의 성격을 바꿉니다.

>>> yielderFunction()
<generator object yielderFunction at 0x07742D28>

yielderFunction()을 호출하면 코드가 실행되지 않지만 생성기가 코드에서 제외됩니다. (가독성을 위해 yielder 접두어로 이름을 지정하는 것이 좋습니다.)

>>> gen = yielderFunction()
>>> dir(gen)
['__class__',
 ...
 '__iter__',    #Returns gen itself, to make it work uniformly with containers
 ...            #when given to a for loop. (Containers return an iterator instead.)
 'close',
 'gi_code',
 'gi_frame',
 'gi_running',
 'next',        #The method that runs the function's body.
 'send',
 'throw']

gi_codegi_frame 필드는 고정 된 상태가 저장되는 필드입니다. dir(..)을 사용하여 이들을 탐색하면 위의 정신 모델이 신뢰할 수 있음을 확인할 수 있습니다.

58
Evgeni Sergeev

모든 대답과 마찬가지로, yield은 시퀀스 생성기를 만드는 데 사용됩니다. 그것은 어떤 시퀀스를 동적으로 생성하는데 사용됩니다. 예를 들어, 네트워크에서 한 줄씩 파일을 읽는 동안 다음과 같이 yield 함수를 사용할 수 있습니다.

def getNextLines():
   while con.isOpen():
       yield con.read()

코드에서 다음과 같이 사용할 수 있습니다.

for line in getNextLines():
    doSomeThing(line)

실행 제어 전송 gotcha

Yield가 실행되면 실행 제어가 getNextLines ()에서 for 루프로 전송됩니다. 따라서 getNextLines ()가 호출 될 때마다 마지막으로 일시 중지 된 시점부터 실행이 시작됩니다.

따라서 간단히 말해 다음 코드를 가진 함수

def simpleYield():
    yield "first time"
    yield "second time"
    yield "third time"
    yield "Now some useful value {}".format(12)

for i in simpleYield():
    print i

인쇄 할 것이다.

"first time"
"second time"
"third time"
"Now some useful value 12"
49

(아래 답변은 스택 및 힙 조작의 일부 트릭을 포함하는 생성기 메커니즘의 기본 구현 가 아니라 _ ​​Python 생성기 사용 관점에서만 설명합니다.

python 함수에서 yield 대신 return을 사용하면 해당 함수는 generator function라는 특수한 것으로 바뀝니다. 이 함수는 generator 유형의 객체를 반환합니다. yield 키워드는 python 컴파일러에게 이러한 함수를 특별히 처리하도록 알리는 플래그입니다. 값이 반환되면 일반 함수는 종료됩니다 . 그러나 컴파일러의 도움으로 생성기 함수 는 재개 가능한 것으로 생각할 수 있습니다. 즉, 실행 컨텍스트가 복원되고 마지막 실행부터 실행이 계속됩니다. 명시 적으로 return을 호출 할 때까지 반복자 프로토콜의 일부인 StopIteration 예외가 발생하거나 함수의 끝에 도달합니다. generator에 대한 많은 참조를 찾았지만 functional programming perspectiveone 이 가장 소화하기 쉽습니다.

(이제 본인의 이해를 바탕으로 generator의 이론적 근거와 iterator에 대해 이야기하고 싶습니다. 이것이 필수 동기를 파악하는 데 도움이되기를 바랍니다. 이터레이터 및 생성기 이러한 개념은 C #과 같은 다른 언어로 표시됩니다.)

내가 알다시피, 많은 데이터를 처리하고 싶을 때, 보통 데이터를 어딘가에 저장 한 다음 하나씩 처리합니다. 그러나이 naive 접근 방식은 문제가 있습니다. 데이터 볼륨이 크면 미리 전체를 저장하는 데 비용이 많이 듭니다. 따라서 data 자체를 직접 저장하는 대신 어떤 종류의 metadata을 간접적으로 저장하지 않는 것이 좋습니다 (예 : the logic how the data is computed.

이러한 메타 데이터를 래핑하는 두 가지 방법이 있습니다.

  1. OO 접근법은 메타 데이터 as a class를 래핑합니다. 이것은 반복자 프로토콜을 구현하는 소위 iterator입니다 (예 : __next__()__iter__() 메소드). 이것은 또한 일반적으로 보이는 반복자 디자인 패턴 입니다.
  2. 기능적인 접근 방식은 메타 데이터 as a function를 래핑합니다. 소위 generator function입니다. 그러나 후드 아래에서 반환 된 generator object 여전히 IS-A 반복자는 반복자 프로토콜도 구현하므로 반복자가 반환됩니다.

어느 쪽이든, 반복자가 생성됩니다. 즉, 원하는 데이터를 제공 할 수있는 객체입니다. OO 접근법은 약간 복잡 할 수 있습니다. 어쨌든 어느 것을 사용할지는 당신에게 달려 있습니다.

46
smwikipedia

수율은 객체입니다

함수에서 return은 단일 값을 반환합니다.

함수가 거대한 값 세트를 반환하기를 원하면 yield을 사용하십시오.

더 중요한 것은, yieldbarrier입니다.

cUDA 언어의 장벽과 마찬가지로, 완료 될 때까지 컨트롤을 전송하지 않습니다.

즉, yield에 도달 할 때까지 함수에서 코드를 처음부터 실행합니다. 그런 다음 루프의 첫 번째 값을 반환합니다.

그런 다음 다른 모든 호출은 함수에서 작성한 루프를 한 번 더 실행하고 반환 할 값이 없을 때까지 다음 값을 반환합니다.

43
Kaleem Ullah

요약하면 yield 문은 함수를 원래 함수의 본문을 둘러싸는 generator이라는 특수 객체를 생성하는 팩토리로 변환합니다. generator이 반복되면 다음 yield에 도달 할 때까지 함수를 실행 한 다음 실행을 일시 중단하고 yield에 전달 된 값으로 평가합니다. 실행 경로가 기능을 종료 할 때까지 각 반복에서이 프로세스를 반복합니다. 예를 들어,

def simple_generator():
    yield 'one'
    yield 'two'
    yield 'three'

for i in simple_generator():
    print i

단순히 산출물

one
two
three

전력은 시퀀스를 계산하는 루프와 함께 발생기를 사용하는 것으로부터 발생하며, 생성기는 매번 계산을 중단 한 다음 루프를 실행하여 다음 계산 결과를 얻습니다. 이렇게하면 즉시 목록을 계산하여 이점이 메모리가됩니다 특히 큰 계산을 위해 저장 됨

Iterable 범위의 숫자를 생성하는 range 함수를 직접 만들고 싶다면 그렇게 할 수 있습니다.

def myRangeNaive(i):
    n = 0
    range = []
    while n < i:
        range.append(n)
        n = n + 1
    return range

이런 식으로 사용하십시오.

for i in myRangeNaive(10):
    print i

그러나 이것은 비효율적입니다. 왜냐하면

  • 한 번만 사용하는 배열을 만듭니다 (이렇게하면 메모리가 낭비됩니다)
  • 이 코드는 실제로 해당 배열을 두 번 반복합니다! :(

다행스럽게도 Guido와 그의 팀은 발전기를 개발할만큼 충분히 관대했기 때문에 우리는 이것을 할 수있었습니다.

def myRangeSmart(i):
    n = 0
    while n < i:
       yield n
       n = n + 1
    return

for i in myRangeSmart(10):
    print i

이제 각 반복에서 next()이라고하는 생성기의 함수는 함수가 종료되고 값을 '산출'하거나 함수의 끝에 도달하는 'yield'문에 도달 할 때까지 함수를 실행합니다. 이 경우 첫 번째 호출에서 next()은 yield 문까지 실행하고 'n'을 반환합니다. 다음 호출에서는 increment 문을 실행하고 'while'으로 돌아가서 평가하고, 참이면 중지합니다. 'n'을 다시 산출하면 while 조건이 false를 반환하고 생성기가 함수의 끝으로 점프 할 때까지 계속됩니다.

42
redbandit

많은 사람들이 return 대신 yield을 사용하지만, 어떤 경우에는 yield이 더 효율적이고 쉽게 작업 할 수 있습니다.

다음은 yield이 가장 적합한 예입니다.

return (기능 상)

import random

def return_dates():
    dates = [] # With 'return' you need to create a list then return it
    for i in range(5):
        date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
        dates.append(date)
    return dates

yield (함수 내)

def yield_dates():
    for i in range(5):
        date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
        yield date # 'yield' makes a generator automatically which works
                   # in a similar way. This is much more efficient.

함수 호출

dates_list = return_dates()
print(dates_list)
for i in dates_list:
    print(i)

dates_generator = yield_dates()
print(dates_generator)
for i in dates_generator:
    print(i)

두 함수 모두 똑같은 일을하지만, yield은 5 대신 3 개의 라인을 사용하고 걱정할 변수가 하나 더 적습니다.

코드의 결과입니다 :

 Output

당신이 볼 수 있듯이 두 기능 모두 똑같은 일을합니다. 유일한 차이점은 return_dates()이 목록을 제공하고 yield_dates()이 생성기를 제공한다는 것입니다.

실생활의 예제는 한 줄씩 파일을 읽거나 단순히 생성기를 만들고 싶을 때와 같습니다.

40
Tom Fuller

yield은 함수의 반환 요소와 같습니다. 차이점은 yield 요소가 함수를 생성기로 변환한다는 것입니다. 생성기는 무언가가 '산출'될 때까지 함수처럼 작동합니다. 발전기는 다음에 호출 될 때까지 정지하고 시작한 시점과 정확히 동일하게 계속됩니다. list(generator())을 호출하여 모든 'yielded'값의 시퀀스를 얻을 수 있습니다.

35
Theoremiser

yield 키워드는 단순히 결과를 수집합니다. yieldreturn +=처럼 생각하십시오.

35
Bahtiyar Özdere

다음은 간단한 yield 기반 접근법으로 fibonacci 시리즈를 계산하는 방법입니다.

def fib(limit=50):
    a, b = 0, 1
    for i in range(limit):
       yield b
       a, b = b, a+b

이것을 REPL에 입력하고 나서 그것을 호출하면 신비한 결과를 얻게됩니다 :

>>> fib()
<generator object fib at 0x7fa38394e3b8>

이것은 yield이 파이썬에 신호를 보내서 generator 를 만들려고하기 때문입니다. 즉, 필요에 따라 값을 생성하는 객체입니다.

그렇다면이 값들을 어떻게 생성합니까? 이것은 내장 함수 next을 사용하여 직접 수행하거나 간접적으로 값을 사용하는 구조체에이를 제공하여 수행 할 수 있습니다. 

내장 next() 함수를 사용하여 .next/__next__를 직접 호출하여 생성기가 값을 생성하도록합니다.

>>> g = fib()
>>> next(g)
1
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
5

간접적으로, fibfor 루프, list 초기화 프로그램, Tuple 초기화 프로그램 또는 기타 값을 생성/생성하는 개체를 제공하는 경우에는 더 이상 값을 생성 할 수 없을 때까지 생성자를 "소비합니다" 그리고 그것을 반환합니다) :

results = []
for i in fib(30):       # consumes fib
    results.append(i) 
# can also be accomplished with
results = list(fib(30)) # consumes fib

비슷하게 Tuple 초기화 프로그램을 사용하면 다음과 같습니다. 

>>> Tuple(fib(5))       # consumes fib
(1, 1, 2, 3, 5)

발전기는 그것이 게으름이라는 의미에서 함수와 다릅니다. 그것은 지역의 상태를 유지하고 필요할 때 언제든지 재개 할 수 있도록함으로써 이것을 수행합니다. 

먼저 fib을 호출하여 호출 할 때 :

f = fib()

파이썬은 함수를 컴파일하고 yield 키워드를 발견하고 단순히 생성기 객체를 반환합니다. 그것은별로 도움이되지 않습니다. 

그런 다음 직접 또는 간접적으로 첫 번째 값을 생성하도록 요청하면 yield을 만나기 전까지 yield에 제공된 값을 반환하고 일시 중지합니다. 이것을 더 잘 보여주는 예제를 위해 print 호출을 사용합시다 (Python 2의 경우 print "text"로 대체하십시오).

def yielder(value):
    """ This is an infinite generator. Only use next on it """ 
    while 1:
        print("I'm going to generate the value for you")
        print("Then I'll pause for a while")
        yield value
        print("Let's go through it again.")

이제 REPL에 입력하십시오.

>>> gen = yielder("Hello, yield!")

값을 생성하는 명령을 기다리는 생성기 객체가 있습니다. next을 사용하고 인쇄물을 확인하십시오.

>>> next(gen) # runs until it finds a yield
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'

인용되지 않은 결과는 인쇄 된 것입니다. 인용 된 결과는 yield에서 반환되는 결과입니다. next을 다시 호출하십시오.

>>> next(gen) # continues from yield and runs again
Let's go through it again.
I'm going to generate the value for you
Then I'll pause for a while
'Hello, yield!'

생성기는 yield value에서 일시 중지 된 것을 기억하고 거기에서 다시 시작합니다. 다음 메시지가 출력되고 yield 문에 대한 검색이 다시 수행됩니다 (while 루프로 인해).

32

또 다른 TL; DR

목록의 반복자 : next()은 목록의 다음 요소를 반환합니다.

반복 생성기 : next()은 즉시 다음 요소를 계산합니다 (실행 코드)

next을 호출하여 흐름을 복잡하게함으로써 수율/발전기를 외부에서 수동으로 control flow를 실행하는 방법으로 볼 수 있습니다.

: 발전기는 NOT 정상 함수입니다. 로컬 변수 (스택)와 같은 이전 상태를 기억합니다. 자세한 설명은 다른 답변이나 기사를 참조하십시오. 생성기는 한 번만 반복 일 수 있습니다. yield 없이는 할 수 있지만 니스 만큼은 아니므로 '매우 좋은'언어 설탕으로 간주 될 수 있습니다.

31
Christophe Roussy

쉽게 설명 할 수있는 간단한 예 : yield

def f123():
    for _ in range(4):
        yield 1
        yield 2


for i in f123():
    print i

출력은 다음과 같습니다. 

1 2 1 2 1 2 1 2
29
Gavriel Cohen

yield 는 return과 비슷합니다. 차이점은 다음과 같습니다. 

yield 는 함수를 반복 가능하게 만듭니다 (다음 예제에서는 primes(n = 1) 함수가 반복 가능하게됩니다).
본질적으로 의미하는 것은 다음에 함수가 호출 될 때입니다. 즉, yield expression 행 뒤에있는 곳에서 계속됩니다.

def isprime(n):
    if n == 1:
        return False
    for x in range(2, n):
        if n % x == 0:
            return False
    else:
        return True

def primes(n = 1):
   while(True):
       if isprime(n): yield n
       n += 1 

for n in primes():
    if n > 100: break
    print(n)

위의 예제에서 isprime(n)이 true이면 소수를 반환합니다. 다음 반복에서 다음 줄부터 계속됩니다. 

n += 1  
24
blueray

여기에있는 모든 대답은 훌륭합니다. (가장 많이 투표 된 사람 중 하나) 코드 작동 방식과 관련이 있습니다. 다른 것은 일반적으로 generators와 관련이 있으며 어떻게 작동하는지 설명합니다.

그래서 저는 발전기가 무엇인지 또는 생산량이 무엇인지 반복하지 않을 것입니다. 나는 이것들이 현존하는 위대한 해답들에 의해서 덮여 있다고 생각한다. 그러나 비슷한 코드를 이해하는 데 몇 시간을 소비 한 후에 어떻게 작동하는지 분석해 보겠습니다.

코드가 이진 트리 구조를 통과합니다. 이 나무를 예로 들어 보겠습니다.

    5
   / \
  3   6
 / \   \
1   4   8

그리고 바이너리 검색 트리 순회 (traversal)의 또 다른 간단한 구현 :

class Node(object):
..
def __iter__(self):
    if self.has_left_child():
        for child in self.left:
            yield child

    yield self.val

    if self.has_right_child():
        for child in self.right:
            yield child

실행 코드는 다음과 같이 __iter__를 구현하는 Tree 객체에 있습니다.

def __iter__(self):

    class EmptyIter():
        def next(self):
            raise StopIteration

    if self.root:
        return self.root.__iter__()
    return EmptyIter()

while candidates 문은 for element in tree로 바꿀 수 있습니다. 파이썬은 이것을 다음으로 번역합니다.

it = iter(TreeObj)  # returns iter(self.root) which calls self.root.__iter__()
for element in it: 
    .. process element .. 

Node.__iter__ 함수는 생성자이므로 inside 코드는 반복마다 실행됩니다. 그래서 실행은 다음과 같이 보일 것입니다 :

  1. 루트 요소가 먼저; 자식을 남겨두고 for을 반복하는지 확인하십시오 (첫 번째 iterator 객체이기 때문에 it1이라고 부르 자)
  2. 자식이있어서 for이 실행됩니다. for child in self.left는 Node 객체 자체 (it2) 인 self.left에서 새 반복기를 만듭니다.
  3. 2와 동일한 논리와 새로운 iterator이 생성됩니다 (it3).
  4. 이제 우리는 나무의 왼쪽 끝에 도달했습니다. it3에는 왼쪽 자식이 없으므로 계속 진행되며 yield self.value
  5. 다음에 next(it3)을 호출하면 StopIteration이 발생하고 올바른 자식이 없으므로 존재합니다 (yield가없는 함수의 끝에 도달 함)
  6. it1it2는 여전히 활성화되어 있습니다. 이들은 고갈되지 않고 next(it2)을 호출하면 StopIteration을 발생시키지 않고 값을 생성합니다.
  7. 이제 it2 컨텍스트로 돌아가서 yield child 문 바로 다음에서 중지 된 곳에서 계속되는 next(it2)을 호출합니다. 더 이상 자식이 없기 때문에 계속해서 self.val가됩니다.

여기서 잡는 것은 모든 반복 하위 반복자 생성 트리를 탐색하고 현재 반복기의 상태를 보유한다는 것입니다. 마지막에 도달하면 스택을 가로 지르고 값이 올바른 순서로 반환됩니다 (가장 작은 수율 값이 먼저 나타남).

귀하의 코드 예제는 다른 기술에서 비슷한 것을했습니다. 그것은 모든 자식에 대해 한 요소 목록을 채운 다음 다음 반복시에 그것을 팝하고 현재 개체 (따라서 self)에서 함수 코드를 실행합니다. .

이 전설적인 주제에 조금 기여한 바입니다. 나는이 과정을 이해하기 위해 몇 시간을 보냈다.

10
Chen A.

요약하면 yield 의 사용법은 generator 를 반환한다는 점을 제외하고는 키워드 return 과 유사합니다.
A generator object는 once 에 대해서만 이동합니다.

yield 에는 두 가지 이점이 있습니다. 

  1. 이 값을 두 번 읽을 필요는 없습니다. 
  2. 노드 노드를 모두 메모리에 두지 않고도 많은 자식 노드를 가질 수 있습니다.
8
123

파이썬에서 generators (특별한 타입의 iterators)은 일련의 값을 생성하는 데 사용되고 yield 키워드는 생성기 함수의 return 키워드와 같습니다. 

기타 흥미로운 것은 yield 키워드가 발전기 함수state을 저장하는 것입니다. 

따라서 number이 발생할 때마다 generator을 다른 값으로 설정할 수 있습니다. 

다음은 인스턴스입니다.

def getPrimes(number):
    while True:
        if isPrime(number):
            number = yield number     # a miracle occurs here
        number += 1

def printSuccessivePrimes(iterations, base=10):
primeGenerator = getPrimes(base)
primeGenerator.send(None)
for power in range(iterations):
    print(primeGenerator.send(base ** power))
7
ARGeo

수율

>>> def create_generator():
...    my_list = range(3)
...    for i in my_list:
...        yield i*i
...
>>> my_generator = create_generator() # create a generator
>>> print(my_generator) # my_generator is an object!
<generator object create_generator at 0xb7555c34>
>>> for i in my_generator:
...     print(i)
0
1
4

짧게 에서는 루프가 멈추지 않고 객체 나 변수가 전송 된 후에도 계속 작동 함을 알 수 있습니다 (실행 후 루프가 중지되는 return과는 다릅니다).

6
Gavriel Cohen

비유는 여기서 아이디어를 파악하는 데 도움이 될 수 있습니다.

하루에 수천 개의 전구를 생성 할 수있는 놀라운 기계를 만들었다 고 상상해보십시오. 이 기계는 고유 한 일련 번호가있는 상자에 전구를 생성합니다. 이러한 모든 전구를 동시에 저장할 수있는 충분한 공간이 없습니다 (예 : 저장 용량 제한으로 인해 기기의 속도를 따라갈 수 없음). 따라서이 기기를 조정하여 필요시 전구를 생성 할 수 있습니다.

파이썬 생성기는이 개념과 많이 다르지 않습니다.

상자에 고유 한 일련 번호를 생성하는 x 함수가 있다고 가정 해보십시오. 분명히, 함수에 의해 생성 된 매우 많은 수의 바코드를 가질 수 있습니다. 더 현명하고 공간 효율적인 옵션은 필요에 따라 일련 번호를 생성하는 것입니다.

기계 코드 :

def barcode_generator():
    serial_number = 10000  # Initial barcode
    while True:
        yield serial_number
        serial_number += 1


barcode = barcode_generator()
while True:
    number_of_lightbulbs_to_generate = int(input("How many lightbulbs to generate? "))
    barcodes = [next(barcode) for _ in range(number_of_lightbulbs_to_generate)]
    print(barcodes)

    # function_to_create_the_next_batch_of_lightbulbs(barcodes)

    produce_more = input("Produce more? [Y/n]: ")
    if produce_more == "n":
        break

우리가 볼 수 있듯이 매번 다음 고유 일련 번호를 생성하는 자체 "함수"가 있습니다. 이 함수는 제너레이터를 반환합니다! 보시다시피 새 일련 번호가 필요할 때마다 함수를 호출하지 않지만 생성자가 다음 일련 번호를 얻기 위해 next()을 사용하고 있습니다.

산출:

How many lightbulbs to generate? 5
[10000, 10001, 10002, 10003, 10004]
Produce more? [Y/n]: y
How many lightbulbs to generate? 6
[10005, 10006, 10007, 10008, 10009, 10010]
Produce more? [Y/n]: y
How many lightbulbs to generate? 7
[10011, 10012, 10013, 10014, 10015, 10016, 10017]
Produce more? [Y/n]: n
6
Rafael

yield 파이썬에서 사용할 수있는 Generator 유형입니다.

수확량이 실제로하는 일을 볼 수있는 링크입니다. 생성기 및 수율 키워드 - Python Central (PC)

또한 yieldreturn처럼 작동하지만 return과는 다른 방식으로 작동합니다. 다른 하나를 잘 이해하지 못하면 yield을 더 자세히 설명하는 링크가 있습니다. 수익률 향상 - jeffknupp

3

가장 간단한 말로 'yield'는 값을 '반환'하는 것과 유사하지만 Generator에서 작동합니다.

1
user3701435

간단한 yield에서는 값 대신 generator 객체를 반환합니다. 

간단한 예제가 도움이 될 것입니다!

def sim_generator():
    for i in range(3):
        yield(i)

obj = sim_generator()
next(obj) # another way is obj.__next__()
next(obj)
next(obj)

위의 코드는 0, 1, 2를 반환합니다.

또는 심지어 짧은

for val in sim_generator():
    print(val)

0, 1, 2를 반환

희망이 도움이

1
Vivek Ananthan

간단한 생성기 함수

def my_gen():
    n = 1
    print('This is printed first')
    # Generator function contains yield statements
    yield n

    n += 1
    print('This is printed second')
    yield n

    n += 1
    print('This is printed at last')
    yield n

yield 문은 함수를 일시 정지하고 모든 상태를 저장하며 나중에 연속 호출에서 계속됩니다.

https://www.programiz.com/python-programming/generator

0
Savai Maheshwari

yield은 무언가를 산출합니다. 마치 누군가가 5 컵짜리 케이크를 만들 것을 요구하는 것과 같습니다. 적어도 한 잔의 컵 케이크로 끝내면 다른 케이크를 만들 때 먹을 수 있습니다.

In [4]: def make_cake(numbers):
   ...:     for i in range(numbers):
   ...:         yield 'Cake {}'.format(i)
   ...:

In [5]: factory = make_cake(5)

여기 factory은 발전기라고 불리며 케이크를 만듭니다. make_function를 호출하면 해당 함수를 실행하는 대신 생성기를 얻습니다. yield 키워드가 함수에 있으면 생성기가되기 때문입니다.

In [7]: next(factory)
Out[7]: 'Cake 0'

In [8]: next(factory)
Out[8]: 'Cake 1'

In [9]: next(factory)
Out[9]: 'Cake 2'

In [10]: next(factory)
Out[10]: 'Cake 3'

In [11]: next(factory)
Out[11]: 'Cake 4'

그들은 모든 케이크를 먹었지 만, 그들은 다시 하나를 요구합니다.

In [12]: next(factory)
---------------------------------------------------------------------------
StopIteration                             Traceback (most recent call last)
<ipython-input-12-0f5c45da9774> in <module>
----> 1 next(factory)

StopIteration:

그리고 그들은 더 많은 것을 요구하는 것을 멈추라는 말을 듣고있다. 그래서 당신이 발전기를 소비하면 당신은 그것을 끝내게됩니다. 케이크를 더 원한다면 make_cake를 다시 부르면됩니다. 컵 케이크를 주문하는 것과 같습니다.

In [13]: factory = make_cake(3)

In [14]: for cake in factory:
    ...:     print(cake)
    ...:
Cake 0
Cake 1
Cake 2

위와 같은 생성기를 사용하여 for 루프를 사용할 수도 있습니다.

한 가지 더 예 : 요청할 때마다 임의의 암호를 원한다고 가정 해 보겠습니다.

In [22]: import random

In [23]: import string

In [24]: def random_password_generator():
    ...:     while True:
    ...:         yield ''.join([random.choice(string.ascii_letters) for _ in range(8)])
    ...:

In [25]: rpg = random_password_generator()

In [26]: for i in range(3):
    ...:     print(next(rpg))
    ...:
FXpUBhhH
DdUDHoHn
dvtebEqG

In [27]: next(rpg)
Out[27]: 'mJbYRMNo'

여기서 rpg은 무작위의 무작위 암호를 생성 할 수있는 생성기입니다. 따라서 유한 요소 수를 가진 목록과 달리 시퀀스의 길이를 모르는 경우 생성기가 유용하다고 말할 수 있습니다.

0
thavan