[python] “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)가 작성했습니다. 이것은 완전한 소스에 대한 링크입니다 : Module mspace .



답변

무엇을 이해하려면 생성기yield무엇인지 이해해야합니다 . 그리고 생성자를 이해하기 전에 반복 가능한 것을 이해해야합니다 .

이터 러블

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

>>> 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

()대신에 사용한 것을 제외하고는 동일합니다 []. 그러나 발전기는 한 번만 사용할 수 있으므로 두 번째로 수행 할 수 없습니다. 제로는 for i in mygenerator0을 계산 한 다음 잊어 버리고 1을 계산하고 4를 하나씩 계산하지 않습니다.

수율

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

>>> 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 the 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 the 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 candidate's 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. 자녀가 많을 수 있으며 자녀가 모두 메모리에 저장되는 것을 원하지 않습니다.

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

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

발전기 피로 제어

>>> 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 에 대한 자세한 내용이 있습니다 .


답변

이해의 지름길 yield

yield명령문이 포함 된 함수를 볼 때 다음과 같은 간단한 트릭을 적용하여 발생하는 상황을 이해하십시오.

  1. result = []함수 시작 부분에 줄 을 삽입하십시오 .
  2. 각각을 yield expr로 바꿉니다 result.append(expr).
  3. return result함수의 맨 아래에 줄 을 삽입하십시오 .
  4. 예, 더 이상 yield진술이 없습니다 ! 코드를 읽고 파악하십시오.
  5. 기능을 원래 정의와 비교하십시오.

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

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

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

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

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

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

    호출 iter(mylist)-> 메소드가있는 객체를 반환합니다 next()(또는 __next__()Python 3).

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

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

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

진실은 파이썬이가 원하는 두 단계 위의 언제 수행입니다 돌이 객체의 내용 – 그것은 루프이 될 수 있도록,하지만 그것은 또한 같은 코드가 될 수있다 otherlist.extend(mylist)( otherlist파이썬 목록입니다).

여기 mylist입니다 반복자 는 반복자 프로토콜을 구현하기 때문이다. 사용자 정의 클래스에서 클래스의 __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명령문 대신 세 개의 return명령문 f123()만 있으면 첫 번째 명령문 만 실행되고 함수가 종료됩니다. 그러나 f123()일반적인 기능은 없습니다. f123()호출 되면 yield 문에 값을 반환 하지 않습니다 ! 생성기 객체를 반환합니다. 또한 함수는 실제로 종료되지 않으며 일시 중단 상태가됩니다. for루프가 생성기 객체를 반복하려고 할 때 함수는 yield이전에 반환 된 후 바로 다음 줄에서 일시 중단 상태에서 다시 시작 하고 다음 코드 줄 (이 경우 yield명령문)을 실행하고 다음 줄을 반환합니다. 안건. 이것은 함수가 종료 될 때까지 발생하며,이 시점에서 제너레이터가 상승 StopIteration하고 루프가 종료됩니다.

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

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

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


답변

이런 식으로 생각하십시오 :

이터레이터는 next()메서드 가있는 객체를 가리키는 멋진 용어입니다 . 따라서 항복 함수는 다음과 같습니다.

원본 버전 :

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

더 이해가 되나요? 아니면 더 혼란 스럽습니까? 🙂

나는이 점에 유의해야한다 입니다 설명을 목적으로 지나치게 단순화. 🙂


답변

yield키워드는 두 가지 간단한 사실로 감소된다 :

  1. 컴파일러는 감지하면 yield키워드 어디서나 함수 내부, 그 함수를 통해 더 이상 반환 return문을. 대신 , 즉시 생성기라는 게으른 “보류중인 목록”개체를 반환합니다.
  2. 생성기는 반복 가능합니다. iterable 은 무엇입니까 ? 유사한 그것의 아무것도 list또는 set또는 range또는 DICT – 전경, 내장 된 특정 순서로 각 요소를 방문해 프로토콜 .

요컨대 , 생성기는 게으르고 점증 적으로 보류중인 목록 이며 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]]

makeRangePython과 같은 함수 를 정의 해 봅시다 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.from_iterable아닌 사용하십시오 chain). 예를 들어, 생성기를 사용하여 다음과 같이 무한히 긴 지연 목록을 구현할 수도 있습니다itertools.count() . 자체 구현 def enumerate(iterable): zip(count(), iterable)하거나 yieldwhile 루프 의 키워드로 구현할 수도 있습니다 .

참고 : 발전기는 실제로 다음과 같은 더 많은 것들에 사용될 수 있습니다 코 루틴 또는 비 결정적 프로그래밍 또는 기타 우아한 것들을 구현하는 . 그러나 여기에 제시된 “게으른 목록”관점은 가장 일반적인 용도입니다.


무대 뒤에서

이것이 “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 은 list와 같은 “for-loop의 개념을 이해하는”객체 [1,2,3]이며 iterator 는 요청 된 for-loop와 같은 특정 인스턴스입니다 [1,2,3].__iter__(). 발생기 가 (기능 구문) 기록 된 방법을 제외하고, 정확하게 어떤 반복기와 동일하다.

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

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

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

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


답변

뭐라고합니까 yield키워드는 파이썬에서합니까?

답변 개요 / 요약

  • yield호출 하면를 가진 함수 Generator를 반환합니다 .
  • 생성기는 반복자 프로토콜 을 구현하므로 반복자이므로 반복자 를 반복 할 수 있습니다.
  • 또한 생성기는 정보를 전송 하여 개념적으로 코 루틴으로 만들 수 있습니다 .
  • Python 3에서는을 사용 하여 한 발전기에서 다른 발전기로 양방향으로 위임 할 수 있습니다 yield from.
  • (부록은 상위 답변을 포함하여 두 가지 답변을 비판 return하고 발전기에서 의 사용에 대해 설명합니다 .)

발전기 :

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

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

yielditerator 프로토콜구현 하는 쉬운 방법을 제공하며 다음 두 가지 방법으로 정의됩니다 :
__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파이썬 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

당신의 정확한 의미에 대해 자세히 읽을 수 yield from있는 PEP (380).

다른 방법 : 닫기 및 던지기

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__ 반환하는 메서드 가 있습니다. 반복자는 제공 (파이썬 2 또는 암시 적으로 호출 (파이썬 3) 메소드 가 제기 될 때까지 루프를.next.__next__forStopIteration , 그리고 그것을 수행하면, 그것은 그렇게 할 것입니다.
  • 그런 다음 생성기 표현식을 사용하여 생성기가 무엇인지 설명합니다. 제너레이터는 단순히 이터레이터 를 만드는 편리한 방법 이므로 문제를 혼동하기 만했지만 아직 yield부분에 도달하지 못했습니다 .
  • 에서 발전기 고갈 제어 그가 부르는 .next대신 그가 내장 기능을 사용할시기, 방법,next . 그의 코드는 Python 3에서 작동하지 않기 때문에 적절한 간접 계층이 될 것입니다.
  • Itertools? 이것은 전혀 어떤 것과 관련이 없습니다 yield.
  • Python 3 yield 새로운 기능과 함께 제공 되는 메서드에 대한 설명은 없습니다 . 최고 / 허용 답변은 매우 불완전한 답변입니다.yield from

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 | 브리즈번, 오스트레일리아

또한, 이것이 결코 좋은 생각이 아니라는 방향으로 지적하고 있는 뛰어난 문제 (10544) 가 있습니다 (PyPy, Python으로 작성된 Python 구현은 이미 구문 경고를 발생시킵니다).

결론은 CPython 개발자가 달리 말할 때까지 : 발전기 표현이나 이해를 넣지 마십시오 yield.

return발전기 의 진술

에서 파이썬 2 :

생성기 함수에서 return명령문은을 포함 할 수 없습니다 expression_list. 이와 관련하여, 베어 (bare) return는 생성기가 완료되었으며 StopIteration발생하게됩니다.

A expression_list는 기본적으로 쉼표로 구분 된 많은 수의 표현식입니다. 기본적으로 Python 2에서는으로로 제너레이터를 중지 return할 수 있지만 값을 반환 할 수는 없습니다.

에서 파이썬 3 :

생성기 함수 return에서이 명령문은 생성기가 완료되었으며 StopIteration발생하게됩니다. 리턴 값은 (만약 있다면) 구성의 인수로서 사용 StopIteration하고해진다 StopIteration.value속성.

각주

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

  2. 예를 들어, 파이썬 3에서 xrange객체 는 재사용이 가능하기 때문에 반복 가능하더라도 객체 range가 아닙니다 Iterator. 목록과 마찬가지로 해당 __iter__메서드는 반복자 객체를 반환합니다.


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


답변

yield그냥 return-생성기로 모든 것을 반환합니다. 차이점은 다음에 생성기를 호출 할 때 마지막 호출부터 yield명령문에 대한 실행이 시작된다는 것 입니다. 반환과 달리 스택 프레임은 수율이 발생할 때 정리되지 않지만 제어가 호출자에게 다시 전송되므로 다음에 함수가 호출 될 때 상태가 다시 시작됩니다.

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

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


답변

언급해야 할 것이 하나 더 있습니다. 수율 함수는 실제로 종료 할 필요가 없습니다. 다음과 같은 코드를 작성했습니다.

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);

실제로 일부 문제를 단순화하고 작업하기가 더 쉽습니다.