プログラミング

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文へ進んでみてください。

← 記事一覧へ戻る