アイテムのリストがあり、最初のいくつかを反復したいとします。
items = list(range(10)) # I mean this to represent any kind of iterable.
limit = 5
他の言語から来たPythonnaïfは、おそらくこの完全にサービス可能でパフォーマンスの高い(ユニディオマティックの場合)コードを書くでしょう。
index = 0
for item in items: # Python's `for` loop is a for-each.
print(item) # or whatever function of that item.
index += 1
if index == limit:
break
しかしPythonには列挙型があり、そのコードの約半分がうまく含まれています。
for index, item in enumerate(items):
print(item)
if index == limit: # There's gotta be a better way.
break
そのため、余分なコードを半分に削減しました。しかし、もっと良い方法があります。
Enumerateが別のオプションのstop
引数を取る場合(たとえば、start
引数を取る場合:enumerate(items, start=1)
)、それは理想的だと思いますが、以下は存在しません( 列挙に関するドキュメント )を参照してください:
# hypothetical code, not implemented:
for _, item in enumerate(items, start=0, stop=limit): # `stop` not implemented
print(item)
index
を参照する必要がないため、名前を付ける必要がないことに注意してください。
上記を書く慣用的な方法はありますか?どうやって?
2番目の質問:なぜこれはenumerateに組み込まれないのですか?
Pythonでループの反復を制限するにはどうすればよいですか?
for index, item in enumerate(items): print(item) if index == limit: break
上記をより短く、慣用的に書く方法はありますか?どうやって?
Zip
は、その引数の最短反復可能要素で停止します。 (最長の反復可能を使用するZip_longest
の動作とは対照的です。)
range
は、プライマリの反復可能オブジェクトとともにZipに渡すことができる限定された反復可能オブジェクトを提供できます。
そのため、range
オブジェクト(およびstop
引数)をZip
に渡し、限定列挙のように使用できます。
Zip(range(limit), items)
Python 3を使用すると、Zip
およびrange
は、中間ステップのリストにデータを具体化する代わりに、データをパイプライン処理する反復可能要素を返します。
for index, item in Zip(range(limit), items):
print(index, item)
Python 2で同じ動作を得るには、xrange
をrange
に、Zip
をitertools.izip
に置き換えるだけです。
from itertools import izip
for index, item in izip(xrange(limit), items):
print(item)
itertools.islice
itertools.islice
を使用できます:
for item in itertools.islice(items, 0, stop):
print(item)
インデックスに割り当てる必要はありません。
enumerate(islice(items, stop))
を構成してインデックスを取得するパブロ・ルイス・ルイスが指摘するように、列挙型でisliceを作成することもできます。
for index, item in enumerate(islice(items, limit)):
print(index, item)
なぜこれが
enumerate
に組み込まれないのですか?
純粋なPythonに実装された列挙を以下に示します(コメントで目的の動作を得るために可能な変更を加えています)。
def enumerate(collection, start=0): # could add stop=None
i = start
it = iter(collection)
while 1: # could modify to `while i != stop:`
yield (i, next(it))
i += 1
列挙を使用している場合、上記の方法はパフォーマンスが低下します。これは、すべての反復を停止する時間かどうかを確認する必要があるためです。停止引数を取得しない場合は、古い列挙を確認して使用できます。
_enumerate = enumerate
def enumerate(collection, start=0, stop=None):
if stop is not None:
return Zip(range(start, stop), collection)
return _enumerate(collection, start)
この追加のチェックは、パフォーマンスへの影響をわずかに無視できます。
whyenumerateには停止引数がないため、これは元々提案されていました( PEP 279 を参照):
この関数は元々、オプションの開始引数と停止引数で提案されました。 GvR [Guido van Rossum]は、関数呼び出し
enumerate(seqn, 4, 6)
には、シーケンスの4番目と5番目の要素を返すスライスとして別のもっともらしい解釈があると指摘しました。あいまいさを避けるために、ループカウンターとしての柔軟性を失うことを意味する場合でも、オプションの引数は削除されました。その柔軟性は、次のように、1から数える一般的な場合に最も重要でした。for linenum, line in enumerate(source,1): print linenum, line
明らかに、start
は非常に価値があるため保持され、stop
は使用ケースが少なく、新しい関数の使用法の混乱に寄与したため削除されました。
別の答えは言う:
なぜ単純に使用しないのですか
for item in items[:limit]: # or limit+1, depends
いくつかの欠点があります:
制限を理解し、それがコピーを作成するかビューを作成するかを理解する場合にのみ、添え字表記を使用したスライスを使用してください。
Pythonコミュニティはenumerateの使用法を知っているので、混乱のコストは引数の値を上回ると思います。
それまでは、次を使用できます。
for index, element in Zip(range(limit), items):
...
または
for index, item in enumerate(islice(items, limit)):
...
または、インデックスがまったく必要ない場合:
for element in islice(items, 0, limit):
...
また、制限を理解していない限り、添え字表記によるスライスを避けてください。
これには itertools.islice
を使用できます。 start
、stop
、およびstep
引数を受け入れます。1つの引数のみを渡す場合、stop
と見なされます。そして、それはどんなイテレータでも動作します。
itertools.islice(iterable, stop)
itertools.islice(iterable, start, stop[, step])
デモ:
>>> from itertools import islice
>>> items = list(range(10))
>>> limit = 5
>>> for item in islice(items, limit):
print item,
...
0 1 2 3 4
ドキュメントの例:
islice('ABCDEFG', 2) --> A B
islice('ABCDEFG', 2, 4) --> C D
islice('ABCDEFG', 2, None) --> C D E F G
islice('ABCDEFG', 0, None, 2) --> A C E G
なぜ単純に使用しないのですか
for item in items[:limit]: # or limit+1, depends
print(item) # or whatever function of that item.
これは一部の反復可能オブジェクトに対してのみ機能しますが、リストを指定したため機能します。
セットやディクテーションなどを使用すると機能しません。
次のように、制限またはリストの最後のいずれか早い方までループしないのはなぜですか?
items = range(10)
limit = 5
for i in range(min(limit, len(items))):
print items[i]
出力:
0
1
2
3
4
列挙内で制限付きのisliceを渡す
a = [2,3,4,2,1,4]
for a, v in enumerate(islice(a, 3)):
print(a, v)
出力:
0 2
1 3
2 4