coroutine - Python ジェネレータ 終了 - "yield "キーワードの役割は?

Python yield None / python / iterator / generator / yield

Pythonでの 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

iliketocode



Answer #1
>>> def func():
...     yield 'I am'
...     yield 'a generator!'
... 
>>> type(func)                 #yieldのある関数はまだ関数です
<type 'function'>
>>> gen = func()
>>> type(gen)                  #しかしそれはジェネレータを返します
<type 'generator'>
>>> hasattr(gen, '__iter__')   #それは反復可能です
True
>>> hasattr(gen, 'next')       #および.nextを使用(Python 3では.__ next__)
True                           # implements the iterator protocol.

ジェネレータタイプはイテレータのサブタイプです。

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

そして必要に応じて、このようにタイプチェックを行います。

>>> isinstance(gen, types.GeneratorType)
True
>>> isinstance(gen, collections.Iterator)
True
>>> 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
def func(an_iterable):
    yield from an_iterable
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)
>>> first_year_interest = next(my_account)
>>> first_year_interest
50.0
>>> next_year_interest = my_account.send(first_year_interest + 1000)
>>> next_year_interest
102.5

def money_manager(expected_rate):
    #.send()からデポジットされた値を受け取る必要があります:
    under_management = yield                   #yieldNoneを開始します。
    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'''
            raise
        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)      #<-manager.send(None)と同じ
    #これは、最初の預金をマネージャーに送る場所です。
    manager.send(deposited)
    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) # -> 60.0

ここで、さらに1,000円を口座に追加し、口座のリターン(60.0)を加えてシミュレーションしてみましょう。

next_year_return = my_account.send(first_year_return + 1000)
next_year_return # 123.6
my_account.close()

また、ジェネレーターで処理したり、ユーザーにフィードバックしたりできる例外を投げることもできます。

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 6, in money_manager
  File "<stdin>", line 2, in <module>
ValueError

現在、この文法では、リスト内包の任意の表現が可能です。

expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
                     ('=' (yield_expr|testlist_star_expr))*)
...
yield_expr: 'yield' [yield_arg]
yield_arg: 'from'テスト| テストリスト

これは、たとえば、 range オブジェクトは再利用できるため、反復可能であっても Iterator はないことを意味します。リストと同様に、それらの __iter__ メソッドはイテレータオブジェクトを返します。