web-dev-qa-db-ja.com

Pythonでジェネレータのn番目の項目を取得します

以下を書くより構文的に簡潔な方法はありますか?

gen = (i for i in xrange(10))
index = 5
for i, v in enumerate(gen):
    if i is index:
        return v

ジェネレーターがgen[index]式。これはリストとして機能しますが、上記のコードと機能的に同じです。

61
Oliver Zheng

1つの方法は itertools.islice

>>> next(itertools.islice(xrange(10), 5, 5 + 1))
5
53
cobbal

これを行うには、ジェネレーターの例としてcountを使用します。

from itertools import islice, count
next(islice(count(), n, n+1))
15
Mark Byers

私は最善の方法だと思います:

next(x for i,x in enumerate(it) if i==n)

(ここで、itはイテレータであり、nはインデックスです)

itertoolsを使用するソリューションのように)インポートを追加したり、(listを使用したソリューションのように)イテレータのすべての要素を一度にメモリにロードしたりする必要はありません。

注1:イテレータのアイテム数がn未満の場合、このバージョンはStopIterationエラーをスローします。代わりにNoneを取得したい場合は、次を使用できます。

next((x for i,x in enumerate(it) if i==n), None)

注2:nextの呼び出し内に角括弧はありません。これはリスト内包表記ではなく、ジェネレーター内包表記であり、n番目の要素よりも元のイテレーターを消費しません。

3
lovasoa

ジェネレータをリストのように扱いたいという誘惑には反対します。シンプルだが素朴なアプローチは、シンプルなワンライナーです:

_gen = (i for i in range(10))
list(gen)[3]
_

ただし、ジェネレータはリストとは異なります。中間結果はどこにも保存されないため、後戻りすることはできません。 python repl:

_>>> gen = (i for i in range(10))
>>> list(gen)[3]
3
>>> list(gen)[3]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: list index out of range
_

シーケンスのn番目の値を取得するためにジェネレーターを使い始めると、ジェネレーターは別の状態になり、n番目の値をもう一度取得しようとすると、異なる結果が返されます。これにより、バグが発生する可能性があります。コード。

質問のコードに基づいて、別の例を見てみましょう。

最初は次のように_4_を2回出力することを期待します。

_gen = (i for i in range(10))
index = 4
for i, v in enumerate(gen):
    if i == index:
        answer = v
        break
print(answer)
for i, v in enumerate(gen):
    if i == index:
        answer = v
        break
print(answer)
_

しかし、これをreplに入力すると、次のようになります。

_>>> gen = (i for i in range(10))
>>> index = 4
>>> for i, v in enumerate(gen):
...     if i == index:
...             answer = v
...             break
... 
>>> print(answer)
4
>>> for i, v in enumerate(gen):
...     if i == index:
...             answer = v
...             break
... 
>>> print(answer)
9
_

そのバグを追跡して頑張ってください。

編集:

指摘したように、ジェネレータが無限に長い場合、それをリストに変換することさえできません。式list(gen)は決して終了しません。

無限に生成されたキャッシングラッパーを無限ジェネレーターの周りに配置して、自由にインデックス付けできる無限に長いリストのように見せることができますが、これには独自の質問と回答が必要であり、パフォーマンスに大きな影響があります。

2
Brad Richardson

使用するのが最適です:例:

_a = gen values ('a','c','d','e')
_

だから答えは次のようになります:

_a = list(a) -> this will convert the generator to a list (it will store in memory)
_

次に、特定のインデックスに移動する場合は、次のようにします。

_a[INDEX] -> and you will able to get the value its holds 
_

カウントのみを知りたい、またはメモリに保存する必要のない操作を実行したい場合は、次のようになります。a = sum(1 in i in a)->これは、所有しているオブジェクトの数をカウントします

もっとシンプルにしてほしい。

1
Ido Bleicher

最初に頭に浮かんだのは、

gen = (i for i in xrange(10))
index = 5

for i, v in Zip(range(index), gen): pass

return v
0
Alexey