24

(内部) イテレータをラップする場合、__iter__メソッドを基礎となるイテラブルに再ルーティングする必要があることがよくあります。次の例を検討してください。

class FancyNewClass(collections.Iterable):
    def __init__(self):
        self._internal_iterable = [1,2,3,4,5]

    # ...

    # variant A
    def __iter__(self):
        return iter(self._internal_iterable)

    # variant B
    def __iter__(self):
        yield from self._internal_iterable

バリアント A と B に大きな違いはありますか? iter()バリアント A は、内部 iterable からクエリされた iterator オブジェクトを返します。バリアント B は、内部 iterable から値を返すジェネレーター オブジェクトを返します。何らかの理由でどちらかが望ましいですか?バージョンで使用されますcollections.abcyield fromバリアントは、return iter()私が今まで使用してきたパターンです。

4

1 に答える 1

18

唯一の重要な違いは、イテラブル内から例外が発生したときに何が起こるかです。return iter()yourを使用するFancyNewClassと例外トレースバックに表示されませんが、使用すると表示されyield fromます。ラッパーを非表示にしたい状況もあるかもしれませんが、トレースバックに関する情報をできるだけ多く持つことは一般的には良いことです。

その他の違い:

  • return iterグローバルから名前をロードする必要がありiterます-これは潜在的に遅く(パフォーマンスに大きな影響を与える可能性は低いですが)、混乱する可能性があります(ただし、そのようにグローバルを上書きする人は誰でも得られるものに値します)。

  • を使用すると、前後にyield from他の式を挿入できます (ただし、同様に を使用できます)。yielditertools.chain

  • 示されているように、yield fromフォームはジェネレーターの戻り値を破棄します (つまりraise StopException(value)、 . 代わりにreturn (yield from iterator).

2 つのアプローチの逆アセンブルを比較し、例外のトレースバックも示すテストを次に示します: http://ideone.com/1YVcSe

使用return iter():

  3           0 LOAD_GLOBAL              0 (iter)
              3 LOAD_FAST                0 (it)
              6 CALL_FUNCTION            1 (1 positional, 0 keyword pair)
              9 RETURN_VALUE
Traceback (most recent call last):
  File "./prog.py", line 12, in test
  File "./prog.py", line 10, in i
RuntimeError

使用return (yield from):

  5           0 LOAD_FAST                0 (it)
              3 GET_ITER
              4 LOAD_CONST               0 (None)
              7 YIELD_FROM
              8 RETURN_VALUE
Traceback (most recent call last):
  File "./prog.py", line 12, in test
  File "./prog.py", line 5, in bar
  File "./prog.py", line 10, in i
RuntimeError
于 2015-05-12T10:32:29.303 に答える