問題の歴史
enumerate()関数は、シーケンスの要素を反復処理しながら、現在のインデックスを同時に取得するためにPythonに登場しました。これは特に重要で、以前は個別のカウンターを手動で設定することが推奨されていましたが、それはイディオマティックでなく、可読性が低いと見なされていました。
問題
ループでは、現在の値だけでなく、そのシーケンス内でのインデックスを知る必要があることがよくあります。個別の変数(i)を使ってインデックスを手動で管理し、range(len(seq))を呼び出すことは、エラー(インデックスと値の不一致、コードの重複)を引き起こし、可読性を低下させます。
解決策
enumerate()は、現在の要素のタプル(インデックス, 値)を各ステップで返すレイジーイテレータを返します。これを使用することで、インデックスをエレガントかつ信頼性高く操作できます:
colors = ['red', 'green', 'blue'] for idx, color in enumerate(colors): print(idx, color)
反復はゼロから始まりますが、任意の開始値を指定することもできます:
for idx, color in enumerate(colors, start=1): print(idx, color)
主な特徴:
enumerate()は任意のイテラブルシーケンスで機能し、タプル(インデックス, 要素)を返しますstartのオフセットを指定できるため、便利なことがありますenumerate()を使用してインデックスの返却を無効にして値のみを取得することはできますか?
いいえ、enumerate()は常に(インデックス, 値)のペアを返します。値のみが必要な場合は、通常のforループを使用してください:
for value in my_list: print(value)
enumerate()をジェネレーターなどのインデックス不可オブジェクトと一緒に使用することはできますか?
はい、enumerate()はすべてのイテラブルオブジェクト、ジェネレーターを含む、で機能します。インデックス付けは値の出現順に行われます:
def mygen(): for i in range(3): yield chr(ord('a')+i) for idx, val in enumerate(mygen()): print(idx, val) # 0 a, 1 b, 2 c
インデックスの開始を0以外に自動的に設定することはできますか?負の値の場合はどうなりますか?
はい、enumerate()にはstart引数があります。負の値を渡すと、インデックスはその値から始まります:
for idx, x in enumerate(['a', 'b', 'c'], start=-3): print(idx, x) # -3 a, -2 b, -1 c
range(len(seq))を使うこと - これにより、コードがより非イディオマティックで可読性が低下しますチームでは大規模なコードをサポートしており、よく次のように使用しています:
for i in range(len(mylist)): process(i, mylist[i])
利点:
欠点:
リファクタリング後は次のように移行しています:
for idx, val in enumerate(mylist): process(idx, val)
利点:
欠点: