Tech カテゴリのイメージ

IndexError: list index out of range の原因と直し方[Python]

Pythonを書いていて頻繁に出会うのがこのエラーになる。

IndexError: list index out of range

意味はそのままで、リストの範囲外の位置を取りに行った、ということだ。長さ3のリストにlst[3]でアクセスすると出る。Pythonのインデックスは0から始まるので、長さ3なら使えるのは0、1、2までになる。

原因はいくつかのパターンに分かれるので、順に見ていく。

1. ループの範囲が1つ多い

いちばん多いのがこれになる。

lst = [10, 20, 30]
for i in range(len(lst) + 1): # 0,1,2,3 が回る
print(lst[i]) # i=3 で IndexError

range(len(lst))は0から2までを返すのに、+1を足したせいで3まで回ってしまっている。

そもそもインデックスが不要なら、リストを直接回すのがいちばん安全になる。

for x in lst:
print(x)

インデックスも欲しいならenumerateを使う。これなら範囲を自分で計算しないので間違えようがない。

for i, x in enumerate(lst):
print(i, x)

2. リストが空だった

処理の途中でリストが空になっているのに気づかず、先頭を取りに行くパターンになる。

results = [x for x in data if x > 100]
print(results[0]) # 条件に合うものが無いと IndexError

フィルタした結果がゼロ件になるのは普通に起きるので、取り出す前に確認する。

if results:
print(results[0])
else:
print("該当なし")

デフォルト値が欲しいだけなら、nextを使う書き方も便利になる。

first = next((x for x in data if x > 100), None)

ファイルを読んで分割した結果が空になっているケースもよくある。空行や末尾の改行が原因のことが多い。

parts = line.split(",")
value = parts[1] # 空行だと parts は [''] なので IndexError
parts = line.strip().split(",")
if len(parts) >= 2:
value = parts[1]

3. ループ中にリストから要素を削除している

見つけにくいのがこれになる。回している最中にリストの長さが変わるので、途中で範囲外になる。

for i in range(len(lst)):
if lst[i] < 0:
lst.pop(i) # 削除するたびに len が減る → IndexError

削除しながら回すのは避けて、新しいリストを作るのが定石になる。

lst = [x for x in lst if x >= 0]

どうしても元のオブジェクトを保ちたい場合は、スライス代入で中身を入れ替える。

lst[:] = [x for x in lst if x >= 0]

4. 二次元リストの行と列を取り違えている

行列を扱っているときに、行数と列数を逆にして回してしまうパターンになる。

matrix = [[1, 2, 3], [4, 5, 6]] # 2行3列
for i in range(3):
for j in range(2):
print(matrix[i][j]) # i=2 で IndexError

行数はlen(matrix)、列数はlen(matrix[0])になる。

for i in range(len(matrix)):
for j in range(len(matrix[i])):
print(matrix[i][j])

行ごとに長さが違う可能性があるので、len(matrix[0])ではなくlen(matrix[i])にしておくと安全になる。

どこで落ちたかを特定する

原因がすぐわからないときは、落ちる直前の状態を出してしまうのが早い。

try:
print(lst[i])
except IndexError:
print(f"len={len(lst)} i={i} lst={lst}")
raise

長さとインデックスを並べて見れば、1つ多いのか、そもそも空なのかはすぐ判別できる。

まとめ

・IndexErrorは存在しない位置を取りに行ったときのエラー。長さnのリストで使えるのは0からn-1まで
・ループ範囲の+1が最頻の原因。インデックスが不要なら直接ループ、必要ならenumerateを使う
・フィルタ結果や空行の分割で空リストになるケースは、取り出す前に長さを確認する
・ループ中の削除は範囲がずれる。内包表記で作り直すかlst[:]で入れ替える
・二次元では行数len(matrix)と列数len(matrix[i])を取り違えていないか確認する
・原因が読めないときはlenとインデックスを一緒に出力すると一発でわかる