PythonのIndexError list index out of rangeを直す方法
リストの範囲外エラーを、lenと添字・ループの回数・空リスト・入力番号から解決。負の添字とスライスの違いも例で説明します。
3個のデータがあるからitems[3]で最後を取り出す。実はこれ、Pythonでは範囲外です。リストの位置は0から数えるため、3個なら最後は[2]になります。
IndexError: list index out of rangeが出たら、「何個入っているか」と「何番を指定したか」を並べて確認しましょう。今回はPython 3で、ループや入力メニューでも起きるずれを直します。
要素数と最後の番号は違う
scores = [80, 95, 70]
print(scores[3])
この例は次のエラーになります。
IndexError: list index out of range
**インデックス(添字)**は、並んだデータの位置を指定する番号です。scoresとの対応は、次のようになります。
| 取り出したい値 | インデックス | 書き方 |
|---|---|---|
| 1個目の80 | 0 | scores[0] |
| 2個目の95 | 1 | scores[1] |
| 3個目の70 | 2 | scores[2] |
len(scores)は要素数なので3ですが、scores[3]は存在しない4個目を指定します。最後の70が欲しいならprint(scores[2])です。Python公式のシーケンス操作
ここでいうシーケンスは、リストや文字列など、順序を持つデータです。文字列でも存在しない位置を指定するとIndexErrorになります。辞書で存在しないキーを指定した場合は、別のKeyErrorなので区別します。組み込み例外の説明
1. エラー直前に長さと番号を表示する
変数をインデックスに使っていると、見ただけでは範囲内か分かりません。次のように表示します。
scores = [80, 95, 70]
index = 3
print("要素数:", len(scores))
print("指定した番号:", index)
print(scores[index])
エラーの前に「要素数: 3」「指定した番号: 3」と出ます。0以上で取り出す場合、有効な範囲は0 <= index < len(scores)です。「3以下」ではなく「3未満」なのがポイントです。
入力から作ったリストなら、思ったよりデータが少ない可能性もあります。データを消す処理のあとで、以前の番号を使っていないかも確認してください。必要ならその行の直前でリスト自体も表示し、想像と実際を比べます。
2. ループを1回多く回していない?
scores = [80, 95, 70]
for i in range(len(scores) + 1):
print(scores[i])
len(scores) + 1は4です。range(4)でiは0、1、2、3となり、最後のscores[3]で止まります。
番号が必要なら、+ 1を外してrange(len(scores))にします。rangeは終点を含まないため、range(3)が作る値は0、1、2です。rangeの公式説明
値を順に使うだけなら、インデックスを自分で扱わなくても書けます。
scores = [80, 95, 70]
for score in scores:
print(score)
番号と値が必要な場合は、enumerate()が便利です。
scores = [80, 95, 70]
for number, score in enumerate(scores, start=1):
print(f"{number}人目: {score}点")
結果は「1人目: 80点」から「3人目: 70点」です。start=1は表示用の番号を1から数える指定で、リストそのもののインデックスを変える機能ではありません。enumerateの仕様
3. 空リストと負のインデックスを確かめる
空のリストには、最初の要素も最後の要素もありません。
items = []
if items:
print(items[-1])
else:
print("データがありません")
if items:は、中身があるかの確認に使えます。[-1]は最後から1個目ですが、空リストでも必ず何か返す書き方ではありません。データがあることを確かめてから取り出します。
3個のリストなら、負のインデックスは-1、-2、-3まで使えます。-4は範囲外です。負の数字なら何でも安全、というわけではないんですね。
スライスは1個を取る操作と違う
scores = [80, 95, 70]
print(scores[1:100])
これは[95, 70]を返します。[1:100]は、位置1から100の手前までの範囲を取り出すスライスです。範囲の端は実際の長さに合わせて調整されます。対して[100]は1個を指定するため、存在しなければエラーです。目的を変えてエラーだけ消さないようにしましょう。
4. 入力メニューの1番とインデックス0をつなぐ
人向けの番号を、そのままリストへ使うと1個ずれます。3を入力して3個目を選びたいなら、範囲確認のあとで1を引きます。
fruits = ["りんご", "みかん", "ぶどう"]
for number, fruit in enumerate(fruits, start=1):
print(f"{number}. {fruit}")
try:
choice = int(input(f"1〜{len(fruits)}を選んでください: "))
except ValueError:
print("整数を入力してください")
else:
if 1 <= choice <= len(fruits):
print("選んだもの:", fruits[choice - 1])
else:
print("表示された番号の範囲で選んでください")
3ならインデックス2へ変換し、「ぶどう」を表示します。0を先に断る点も大切です。確認せず1を引くと、0が-1になり、意図せず最後の項目を選んでしまいます。文字を入れた場合のValueErrorと、番号の範囲は別に確認しています。
練習:3色を番号付きで表示する
次のコードを、エラーなく0から2の番号と色を表示する形へ直してください。
colors = ["赤", "青", "緑"]
for i in range(len(colors) + 1):
print(i, colors[i])
模範解答
colors = ["赤", "青", "緑"]
for i, color in enumerate(colors):
print(i, color)
0 赤
1 青
2 緑
解説
元のコードでは、最後に存在しないcolors[3]へ進みます。enumerateなら存在する値と番号をセットで取得でき、自分で範囲を合わせる箇所を減らせます。range(len(colors))へ直す方法でも正解です。
リストを作るところから学ぶならリストと辞書の入門、繰り返しを確認するならwhile文とfor文へ進んでみてください。