ジェネレータから1つのアイテムだけを選択する方法(Python)?


213

次のようなジェネレーター関数があります。

def myfunct():
  ...
  yield result

この関数を呼び出す通常の方法は次のとおりです。

for r in myfunct():
  dostuff(r)

私の質問、好きなときにいつでもジェネレータから要素を1つだけ取得する方法はありますか?たとえば、私は次のようなことをしたいと思います:

while True:
  ...
  if something:
      my_element = pick_just_one_element(myfunct())
      dostuff(my_element)
  ...

回答:


304

を使用してジェネレータを作成する

g = myfunct()

アイテムが欲しいときはいつでも、

next(g)

(またはg.next()Python 2.5以下)。

ジェネレーターが終了すると、発生しますStopIteration。必要に応じてこの例外をキャッチするか、次のdefault引数を使用できますnext()

next(g, default_value)

4
注:gの最後の項目が提供された後でg.next()を使用しようとしたときにのみStopIterationが発生します。
Wilduck '19年

26
next(gen, default)StopIteration例外を回避するためにも使用できます。たとえばnext(g, None)、文字列のジェネレーターの場合、反復が終了すると、文字列またはNoneが生成されます。
Attila

8
Python 3000では、next()は__next __()です
ジョナサンボールドウィン

27
@JonathanBaldwin:コメントは誤解を招く可能性があります。Python 3では、私の回答で指定された2番目の構文を使用しますnext(g)。これは内部的に呼び出さg.__next__()れますが、通常len(a)内部的に呼び出されることを気にしないのと同じように、本当に心配する必要はありませんa.__len__()
Sven Marnach 14

14
もっとはっきりしていたはずです。g.next()あるg.__next__()py3kに。ビルトインnext(iterator)はPython 2.6以降で使用されており、すべての新しいPythonコードで使用する必要があります。py<= 2.5をサポートする必要がある場合は、バックインプリメントするのは簡単です。
ジョナサンボールドウィン

29

発電用のひとつの要素を取り出すためbreakfor声明、またはlist(itertools.islice(gen, 1))

あなたの例によれば(文字通り)次のようなことができます:

while True:
  ...
  if something:
      for my_element in myfunct():
          dostuff(my_element)
          break
      else:
          do_generator_empty()

いつでも [一度生成された] ジェネレーターから要素を1つだけ取得したい場合」(50%が元の意図であり、最も一般的な意図だと思います):

gen = myfunct()
while True:
  ...
  if something:
      for my_element in gen:
          dostuff(my_element)
          break
      else:
          do_generator_empty()

このようにして、の明示的な使用をgenerator.next()回避でき、入力の終わりの処理では、(暗号化された)StopIteration例外処理や追加のデフォルト値の比較は必要ありません。

あなたはエンド・オブ・発電機の場合、何か特別な操作を行いたい場合は、ステートメントセクションのみが必要とされています。else:for

next()/ に関する注意.next()

Python3では、この.next()メソッドの名前が.__next__()正当な理由で名前が変更されました:低レベルと見なされました(PEP 3114)。Python 2.6以前は、組み込み関数next()は存在しませんでした。そして、そのまれな必要性と組み込み名の疑わしいインフレのためnext()operatorモジュールに移動することも議論されました(賢明だったでしょう)。

next()デフォルトなしで使用することは、依然として非常に低レベルの習慣StopIterationです。通常のアプリケーションコードでは、あからさまにボルトのように不可解なものを公開します。そしてnext()、デフォルトのセンチネルでの使用-これはnext()直接入力するための唯一のオプションであるべきですbuiltins-は制限されており、奇妙な非Pythonicロジック/読み取り可能性に理由を与えます。

結論:next()の使用は非常にまれです- operatorモジュールの関数を使用するようなものです。使用してfor x in iteratorislicelist(iterator)非常に常に可能と-とイテレータを受け入れる他の機能は、シームレスにアプリケーションレベルでイテレータを使用しての自然な方法です。next()このスレッドの質問が示すように、低レベル、追加の概念、自明ではありません。たとえば、breakin を使用するのforは従来通りです。


8
これは、リスト結果の最初の要素を取得するだけでは、あまりにも多くの作業です。多くの場合、怠惰である必要はありませんが、py3での選択肢はありません。似ているものはありませんかmySeq.head
javadba 2018

2

ジェネレータから任意の値を取得する便利な方法があるとは思いません。ジェネレーターは自身をトラバースするnext()メソッドを提供しますが、メモリを節約するためにシーケンス全体がすぐに生成されるわけではありません。それがジェネレータとリストの機能的な違いです。


1

Python3の完全な動作例のためにこれらの回答をスキャンする人のために...まあここに行く:

def numgen():
    x = 1000
    while True:
        x += 1
        yield x

nums = numgen() # because it must be the _same_ generator

for n in range(3):
    numnext = next(nums)
    print(numnext)

これは出力します:

1001
1002
1003

1

ジェネレータは、イテレータを生成する関数です。したがって、イテレータインスタンスを取得したら、next()を使用してイテレータから次のアイテムをフェッチします。例として、next()関数を使用for inして最初のアイテムをフェッチし、後で残りのアイテムを処理するために使用します。

# create new instance of iterator by calling a generator function
items = generator_function()

# fetch and print first item
first = next(items)
print('first item:', first)

# process remaining items:
for item in items:
    print('next item:', item)

0
generator = myfunct()
while True:
   my_element = generator.next()

最後の要素が取得された後にスローされた例外を必ずキャッチしてください


Python 3では無効です。kxrによる優れた回答を参照してください。
2016

2
ただ、Pythonの3のために、「次の(発電機)」と「generator.nextを()」置き換え
iyop45

-3

唯一の方法は、イテレータからリストを取得して、そのリストから必要な要素を取得することだと思います。

l = list(myfunct())
l[4]

スヴェンの答えはおそらくより良いですが、あなたのニーズにより近い場合に備えて、ここではそのままにしておきます。
keegan3d 2011年

26
これを行う前に、有限のジェネレーターがあることを確認してください。
Seth

6
問題は明らかにO(1)ですが、イテレータの長さは複雑です。
yo

1
あまりにも多くのメモリとプロセスを無駄にしてジェネレータから吸い込むには!また、@ Sethが前述したように、ジェネレーターが生成をいつ停止するかは保証されていません。
pylover 2016

これは明らかにされていない唯一の方法(または場合は、最良の方法myfunct()あなたは組み込みの機能を使用することができますので、多数の値を生成します)next次の生成された値を取得します。
HelloGoodbye 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.