web-dev-qa-db-ja.com

Python 3.3のyieldとともにジェネレーターに戻る

Python 2では、関数定義でreturnとyieldが一緒になったときにエラーが発生しました。ただし、このコードではPython 3.3

def f():
  return 3
  yield 2

x = f()
print(x.__next__())

yieldで関数でreturnが使用されているというエラーはありません。ただし、関数__next__が呼び出されると、例外StopIterationがスローされます。返された値だけではない理由3?この戻り値は何とか無視されますか?

45
scdmb

これはPython 3.3の新しい機能です(コメントノートとして、3.2では動作しません)。ジェネレーターのreturnは、長い間raise StopIteration()と同等でしたが、ジェネレーターのreturn <something>raise StopIteration(<something>)と同等になりました。そのため、表示されている例外はStopIteration: 3として出力される必要があり、値は例外のvalue属性からアクセスできますオブジェクト:(新しい)yield from構文を使用するようにジェネレーターが委任されている場合、それが結果になります詳細については、 PEP 38 を参照してください。

def f():
    return 1
    yield 2

def g():
    x = yield from f()
    print(x)

# g is still a generator so we need to iterate to run it:
for _ in g():
    pass

これは1を出力しますが、2は出力しません。

50
user395760

戻り値は無視されませんが、ジェネレーターのみyield values、returnはジェネレーターを終了します。この場合は早期に終了します。その場合、ジェネレータを進めることはyieldステートメントに決して到達しません。

イテレータが値の「終わり」に達して生成するたびに、StopIterationmustが発生します。ジェネレーターも例外ではありません。 Python 3.3以降、return式は例外の値になります。

_>>> def gen():
...     return 3
...     yield 2
... 
>>> try:
...     next(gen())
... except StopIteration as ex:
...     e = ex
... 
>>> e
StopIteration(3,)
>>> e.value
3
_

next()を直接呼び出すのではなく、.__next__()関数を使用して反復子を進めます。

_print(next(x))
_
27
Martijn Pieters

この答えは質問とはまったく関係ありませんが、ウェブ検索後にここにたどり着く人にとっては役に立つかもしれません。

最終的な戻り値を生成値に変換する小さなヘルパー関数を次に示します。

def generator():
  yield 1
  yield 2
  return 3

def yield_all(gen):
  while True:
    try:
      yield next(gen)
    except StopIteration as e:
      yield e.value
      break

print([i for i in yield_all(generator())])

[1、2、3]


またはデコレーターとして:

import functools

def yield_all(func):
  gen = func()
  @functools.wraps(func)
  def wrapper():
    while True:
      try:
        yield next(gen)
      except StopIteration as e:
        yield e.value
        break
  return wrapper

@yield_all
def a():
  yield 1
  yield 2
  return 3
print([i for i in a()])

[1、2、3]

0