これはなに

Effective Python 第2版、項目7「rangeではなくenumerateを使う」のまとめ。
ポイント

enumerateを使えば、イテレータでループしつつ、要素のインデックスも取り出せるrangeでインデックスをループさせるより、enumerateを使ったほうが簡潔enumerateの第2引数で、インデックスの開始番号を指定できる
rangeとは

rangeはPythonの組み込み関数のひとつ。ループ処理に便利。
for i in range(10):
print(i)リストをループさせる

Pythonのlistは配列。これをループさせるときは、シーケンスを直接ループできる。
languages = ["python", "golang", "VHDL", "C++"]
for lang in languages:
print(lang)リストをループさせつつ、インデックスも取り出すことを考える

リストをループさせつつ要素のインデックスを得たい場合を考える。
rangeを使うと下記のように書ける。
languages = ["python", "golang", "VHDL", "C++"]
for i in range(len(languages)):
lang = languages[i]
print(f"{i + 1} : {lang}")ただし、上記の書き方はステップが多く読みにくい。
Pythonには、こういう時に便利な組み込み関数enumerateがある。
enumerateとは

enumerateはPythonの組み込み関数のひとつ。遅延評価ジェネレータでイテレータをラップし、ループのインデックスとイテレータの次の値の対をyieldする。
公式ドキュメントによると、下記の記述と等価らしい。
def enumerate(iterable, start=0):
n = start
for elem in iterable:
yield n, elem
n += 1つまり、enumerateは、「ループのインデックスとイテレータの次の値の対」を呼び出すたびに生成(遅延評価)するジェネレータを生成する。
呼び出すたびに生成するため、メモリを圧迫せずに処理できる。
enumerateはジェネレータを生成するため、nextを用いて次の値を取得できる。
languages = ["python", "golang", "VHDL", "C++"]
it = enumerate(languages)
print(next(it)) # (0, 'python')
print(next(it)) # (1, 'golang')rangeではなくenumerateを使う

リストをループさせつつ要素のインデックスを得たい場合、enumerateを使うと下記のように書ける。
languages = ["python", "golang", "VHDL", "C++"]
for i, lang in enumerate(languages):
print(f"{i + 1} : {lang}")rangeを使った場合と比較すると、enumerateを使ったほうが簡潔に書ける。
languages = ["python", "golang", "VHDL", "C++"]
# enumerateを使う
for i, lang in enumerate(languages):
print(f"{i + 1} : {lang}")
# rangeを使う
for i in range(len(languages)):
lang = languages[i]
print(f"{i + 1} : {lang}")enumerateのカウント開始の数を変更する

enumerateの第2引数にカウントを開始する数を指定できる。これを利用すると、先のコードはより簡潔に書ける。
languages = ["python", "golang", "VHDL", "C++"]
for i, lang in enumerate(languages, 1):
print(f"{i} : {lang}")