カウンタ変数/インデックスを使ったループ – コンピュータサイエンスの古典です! 一般的には、明示的にカウンタ変数/インデックスを定義して、ループごとに手動でインクリメントするか、拡張された for
ループによってこの処理を避けるためにある種の構文解析を使用することになります。
some_list = ['Looping', 'with', 'counters', 'is', 'a', 'classic!']
# Manual counter incrementation
i = 0
for element in some_list:
print(f'Element Index: {i}, Element: {element}')
i += 1
# Automatic counter incrementation
for i in range(len(some_list)):
print(f'Element Index: {i}, Element: {some_list[i]}')
これらのスニペットは両方とも同じ出力になります。
Element Index: 0, Element: Looping
Element Index: 1, Element: with
Element Index: 2, Element: counters
Element Index: 3, Element: is
Element Index: 4, Element: a
Element Index: 5, Element: classic!
このようなループ処理は日常的によくあることなので、 enumerate()
関数は Python の名前空間に組み込まれています。
Pythonでは、余計な依存関係なしに、次のような簡単な構文で、自動的にカウンタ変数/インデックスを持つイテラブルをループすることができます。
for idx, element in enumerate(some_list):
print(idx, element)
注意: id
は予約キーワードであるため、他のラベルが適用できない場合は、 idx
というインデックス名を付けるのが一般的ですが、必ずしも必要ではありません。
一般的には、作業している反復処理に基づいて、より意味のある名前を付けることができます。
例えば、 batch_num, batch in enumerate(...)
といった具合です。
このコード片は次のような結果になります。
0 Looping
1 with
2 counters
3 is
4 a
5 classic!
それでは、この関数がどのように動作するのか見てみましょう。
これは古典的でよくあるもので、Pythonの真の流儀では、よくある冗長な操作を単純化し、コードの可読性を向上させるものです。
Pythonのenumerate()関数
enumerate()関数は反復可能なコレクション (タプル、リスト、文字列など) を受け取り、
enumerate` オブジェクトを返す。
このオブジェクトはキーセットと値セットからなり、キーはカウンター変数 (0 から始まる) に、値は反復可能コレクションの元の要素に対応する。
obj = enumerate(some_list)
print(type(obj))
# <class 'enumerate'=""
注意: enumerate
オブジェクトは、それ自体が反復可能なものです! 標準の for
シンタックスを使用して、 enumerate
オブジェクトのキーと値をアンパックすることができます。
Python の標準の for
構文を使って、このオブジェクトのキーと値をアンパックして、それらの型を調べることができます。
for key, value in obj:
print(type(key), type(value))
# <class 'int'="" <class 'str'=""
# <class 'int'="" <class 'str'=""
# <class 'int'="" <class 'str'=""
# <class 'int'="" <class 'str'=""
# <class 'int'="" <class 'str'=""
# <class 'int'="" <class 'str'=""
値のデータ型(元のコレクションの要素)は保持されているので、たとえカスタムデータ型を渡したとしても、それが有効な反復可能コレクションであれば、単にカウンター変数でアノテーションされるだけです。
もし、オブジェクトそのものをリストにまとめるとしたら、その構造は非常に明快になるだろう。
print(list(obj))
# [(0, 'Looping'), (1, 'with'), (2, 'counters'), (3, 'is'), (4, 'a'), (5, 'classic!')]
0から始まるカウンタ変数と、そのインデックスにマッピングされた元の反復記号の各要素の、それぞれ2つの要素を持つタプルのセットだけです。
オプションの start
引数を指定することができます。
これは、反復処理可能ファイルの開始インデックスではなく、関数が生成する最初のカウンタ/インデックスの開始値を指定します。
例えば、 0
ではなく 1
から始めたい場合を考えてみましょう。
obj = enumerate(some_list, 1)
print(list(obj))
# [(1, 'Looping'), (2, 'with'), (3, 'counters'), (4, 'is'), (5, 'a'), (6, 'classic!')]
enumerate()でイテラブルをループする。
とはいえ、 enumerate
オブジェクトをループするのは、他のイテレータブルをループするのと同じように見えます。
ここで便利なのが for
ループで、返されたタプルの値に参照変数を代入することができます。
さらに、オブジェクトを明示的に参照する必要はありません。
単一のループの外ではほとんど使用されないので、返された値は通常ループ自体の中で直接使用されます。
# No need to assign the returned `enumerate` object to a distinct reference variable
for idx, element in enumerate(some_list):
print(f'{idx}, {element}')
という結果になります。
0, Looping
1, with
2, counters
3, is
4, a
5, classic!
f-StringとPythonの出力フォーマットについてもっと知りたい方は、Python 3のf-Stringを使った文字列フォーマットガイドをご覧ください! f-Stringを使った文字列フォーマットについてもっと知りたい方は、Python 3’s f-Stringを使った文字列フォーマットガイドをご覧ください!>.
をご覧ください。
反復表の各要素に注釈を付ける、つまり、カウンタをインクリメントしてそれを返す、一方、反復表の要素にアクセスするのは、これと同じくらい簡単です
注目すべきは、enumerate()
関数の中では特別なことは何も起きていないことです。
これは機能的には、最初に書いたループと同じで、明示的にカウンタ変数を要素とともに返しています。
公式ドキュメントの注釈を見ると、この関数の結果は関数的に次のように等価です。
def enumerate(sequence, start=0):
n = start
for elem in sequence:
yield n, elem
n += 1
このコードは、最初に定義した実装と非常によく似ていることがわかると思います。
# Original implementation
i = 0
for element in some_list:
print(f'Element Index: {i}, Element: {some_list[i]}')
i += 1
# Or, rewritten as a method that accepts an iterable
def our_enumerate(some_iterable, start=0):
i = start
for element in some_iterable:
yield i, element
i += 1
ここで重要なのは、yield
キーワードは反復可能なジェネレータを定義していることです。
インデックスと要素そのものを返すことで、反復可能なジェネレータオブジェクトを作成し、それをループして for
ループで要素 (とインデックス) を取り出すことができます。
yield` キーワードの使い方についてもっと知りたい方は、Pythonの “yield” キーワードを理解するためのガイドをご覧ください! Pythonの “yield” キーワードの使い方についてもっと知りたい方は、Pythonの “yield” キーワードを理解するためのガイドをご覧ください。
をご覧ください。
組み込みの関数の代わりに our_enumerate()
関数を使用しても、ほとんど同じ結果になります。
some_list = ['Looping', 'with', 'counters', 'is', 'a', 'classic!']
for idx, element in our_enumerate(some_list):
print(f'{idx}, {element}')
obj = our_enumerate(some_list)
print(f'Object type: {obj}')
この結果、次のようになります。
0, Looping
1, with
2, counters
3, is
4, a
5, classic!
Object type: <generator 0x000002750b595f48="" at="" object="" our_enumerate=""
唯一の違いは、より良いクラス名の代わりに、一般的なジェネレータオブジェクトを持っていることです。
結論
結局のところ、 enumerate()
関数は、非常に一般的で簡単なループの実装を包む、単なる構文上の砂糖に過ぎないのです。
この短いガイドでは、Pythonの enumerate()
関数 – コレクションを繰り返し処理し、インデックスで要素を注釈するための組み込みの便利なメソッド – を見てきました。
</class