静かなる名辞

pythonとプログラミングのこと

2019/03/22:TechAcademyがteratailの質問・回答を盗用していた件
2019/03/26:TechAcademy盗用事件 公式発表と深まる疑念


TypeError: list indices must be integers or slices, not ***等の原因と対処法

はじめに

 pythonを触り始めたばかりの人は、よくこんなエラーに遭遇すると思います。

  • TypeError: list indices must be integers or slices, not ***

 ***の部分はfloatだったりlistだったりstrだったりといろいろありますが、とにかくこんなエラーです。

 また、次のも見たことがあるかもしれません。

  • TypeError: slice indices must be integers or None or have an __index__ method

 これはスライス(:を使ってリストの特定範囲だけ抜き出すやつ)を使ったときに出てきます。まあ、似たようなものです。

 この記事ではこういうエラーの原因と対処法について説明します。

原因

 さて、簡単にこのエラーを再現してみましょう。

>>> lst = [0, 1, 2, 3]
>>> lst["hoge"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: list indices must be integers or slices, not str

 出ましたね。

 今回は***の部分はstrになっていますが、これは「indices」([]の中に入るもの)として渡されたものが文字列で、でもlistのindicesは整数かスライスじゃないとだめだよ、というエラーです。

 また、スライスについても同様で、このように再現できます。

>>> lst["fuga":]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: slice indices must be integers or None or have an __index__ method

 もうある程度は気づいたと思いますが、[](添字表記と言ったりします)の中に入れられるものは型が決まっています。そして、それ以外の型のオブジェクトを入れるとエラーになります。

対処法

 上述のような理由で出てくるので、はっきり言って対処法はケースバイケースです。なので、状況に応じてデバッグをする必要があります。

 とりあえず、変数を使っているようなケースでは、printしてみましょう。一番単純なデバッグです。

>>> for i in "hoge":  # エラーになってしまう
...     print(lst[i])
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: list indices must be integers or slices, not str
>>> for i in "hoge":
...     print(type(i), i)  # iの型と値をprintするコードを追加
...     print(lst[i])
... 
<class 'str'> h  # str型のhが渡っていることがわかる
Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
TypeError: list indices must be integers or slices, not str

 これを見ればなんとなくわかることもありますが、もう少し想像力が必要なケースも多いでしょう。

 ありがちなのは、インデックスでループさせたつもりだったけど元のリストをループさせていたとかでしょうか。

>>> s = ["hoge", "fuga"]
>>> for i in s:
...     print(s[i])
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: list indices must be integers or slices, not str

 pythonのforはループ対象から1つずつ要素を取り出す、という動作をします(他の言語のforeachなどを想像されるとわかりやすいかもしれません)。

 この場合は、以下のように書きます。

>>> s = ["hoge", "fuga"]
>>> for i in range(len(s)):  # rangeとlenを組み合わせてインデックスを作る
...     print(s[i])
... 
hoge
fuga
>>> for x in s:  # 単にこれも可
...     print(x)
... 
hoge
fuga

 rangeは「0から引数の整数-1のイテラブル(ループで要素を取り出して使えるもの)を作るクラス」、lenは「イテラブルの長さを取得する関数」ですね。下の方法の方がスマートですが、インデックスを使った複雑なループでは上の方法にも分があるケースがあります。

 あるいは、「割り算や掛け算の結果をインデックスに使ってしまった」場合も、こういうエラーは起こりえます。特に割り算には注意です。

>>> lst = list(range(10))
>>> lst
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> for i in range(10):
...     print(lst[i/2])
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: list indices must be integers or slices, not float

 「整数/整数」は浮動小数点数を返します。整数で結果を得たいときは//を使うか、int, math.floor, math.ceil, roundなどを使って丸めてください。

math --- 数学関数 — Python 3.7.4 ドキュメント
組み込み関数 — Python 3.7.4 ドキュメント

>>> for x in range(10):
...     print(lst[x//2])
... 
0
0
1
1
2
2
3
3
4
4

 また、掛け算はどちらかのオペランドが浮動小数点数なら結果も浮動小数点数になりますので、やはりint型への変換が必要になります。

似たようなもの

 大半のイテラブルは添字表記で整数型しか使えません。

>>> (0,1,2)["hoge"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: tuple indices must be integers or slices, not str
>>> "hoge"["hoge"]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: string indices must be integers

 対処の方針は同じです。

 辞書のつもりでリストを作成していた、あるいはsetを作成していたというケースもありそうです。そういったケースでは、そもそも添字表記の対象とするオブジェクトの作り方が間違っていないのかというところから見直す必要があります。

まとめ

 わかってしまえばなんてことはないエラーなのですが、ぱっと見なにを言っているのかよくわからないので戸惑う、ということも初心者の方にはありがちだと思います。とにかくlistや大半のイテラブルは添字表記に整数、またはスライス(これも整数しか受け付けません)しか受け付けない、そこに整数以外が入るとエラーになる、ということを理解してコードを書くことが大切だと思います。