年齢を表示しようとしただけなのに、TypeError: can only concatenate str (not "int") to str と出て止まってしまった。Pythonを始めたばかりのころ、こんな経験はありませんか?
英語のメッセージがずらっと並ぶと、それだけで身構えてしまいますよね。けれどTypeErrorは、メッセージに原因のヒントがかなり具体的に書かれている、読みやすい部類のエラーです。
この記事では、TypeErrorの意味からメッセージの読み方、よくある原因と直し方までを順番に見ていきます。最後には、エラーを未然に防ぐための考え方も紹介します。
サンプルの実行結果は、手元のPython 3.11、3.12、3.13で確認したものです。この記事で紹介するメッセージは、3つのバージョンで同じ表示でした。
TypeErrorとはどんなエラーなのか¶
まずは、TypeErrorが何を知らせているのかを押さえておきましょう。ひとことで言うと、値の型がその操作に合っていないときに出るエラーです。
Pythonの値には、整数のint、文字列のstr、リストのlistといった型があります。そして型ごとに、できる操作とできない操作が決まっています。
たとえば数値どうしは足し算ができますし、文字列どうしもつなげられます。ところが数値と文字列を足そうとすると、Pythonはどちらのルールで計算すればよいのか判断できません。
公式ドキュメントでも、TypeErrorは操作や関数が不適切な型のオブジェクトに適用されたときに送出されると説明されています。型の基本がまだあいまいな方は、先に型変換の記事を読んでおくと理解が早くなります。 【関連記事】Pythonの型変換とは?int()・float()・str()・bool()の使い方と、int("3.5")がエラーになる落とし穴
ValueErrorとの違い¶
TypeErrorとよく混同されるのが、ValueErrorです。2つの違いは、型が合っているかどうかにあります。
違いを表で整理すると、次のようになります。
| エラー | 起きる状況 | 例 |
|---|---|---|
| TypeError | 値の型がそもそも合っていない | int(None) |
| ValueError | 型は合っているが中身がおかしい | int("abc") |
| AttributeError | その型が持っていない属性を使った | 文字列に append() |
int("abc")は、文字列を受け取る関数に文字列を渡しているので型は正しいのです。ただ中身が数字ではないので、ValueErrorになります。
一方でint(None)は、そもそもNoneという型を受け付けていません。だからTypeErrorになるわけです。
エラーメッセージの読み方¶
次に、メッセージのどこを見ればよいのかを確認しましょう。冒頭の例を、実際のコードで再現してみます。
age = 20
print("年齢は" + age + "歳です")
これを実行すると、最後の行に次のメッセージが表示されます。
TypeError: can only concatenate str (not "int") to str
直訳すると、文字列には文字列しかつなげられません、intは無理です、という意味になります。つまり、文字列の足し算にint型の値が混ざっていると教えてくれているのです。
TypeErrorのメッセージには、ほとんどの場合関係している型の名前が入っています。まずは型の名前を探して、自分が想定していた型と同じかどうかを確かめるのが近道です。
どの行でエラーが起きたのかは、メッセージより上に表示されるtracebackで確認できます。tracebackの読み方に自信がない方は、こちらもあわせてどうぞ。 【関連記事】Pythonのエラー文はどこを読めばいい?初心者向けtracebackの見方
TypeErrorのよくある原因一覧¶
TypeErrorにはいくつかの定番パターンがあります。エラーが出たら、まずは次の表からメッセージが似ているものを探してみてください。
| メッセージ | 主な原因 |
|---|---|
| can only concatenate str (not "int") to str | 文字列と数値を + でつないだ |
| unsupported operand type(s) for +: 'int' and 'str' | 数値と文字列を足し算した |
| 'NoneType' object is not subscriptable | Noneに [0] などでアクセスした |
| 'list' object is not callable | 組み込み関数の名前を変数で上書きした |
| missing 1 required positional argument | 関数に渡す引数が足りない |
| unhashable type: 'list' | リストを辞書のキーや集合の要素にした |
| 'int' object is not iterable | 数値をfor文で回そうとした |
表の中には、メッセージだけでは原因が想像しにくいものもあります。ここからは、とくに初心者がつまずきやすいものを順番にくわしく見ていきます。
原因1 文字列と数値を混ぜて計算した¶
いちばん多いのが、この文字列と数値の混ぜ合わせです。とくにinput()で受け取った値は、数字を入力しても必ず文字列になる点に注意が必要です。
たとえば次のコードは、10と入力してもTypeErrorになります。
price = input("金額を入力してください:")
total = price + 100
input()の戻り値は文字列の"10"なので、数値の100とは足せません。このときのメッセージは can only concatenate str (not "int") to str です。
直し方は、どちらの型にそろえたいかで変わります。計算したいなら数値に、表示用につなげたいなら文字列にそろえましょう。
price = int(input("金額を入力してください:"))
total = price + 100
age = 20
print(f"年齢は{age}歳です")
文章に数値を埋め込みたいときは、f-stringを使うと型を気にせず書けます。str()で変換して + でつなぐより読みやすくなるので、私はほとんどこちらを使っています。
ちなみに、sum(["1", "2"])のように文字列のリストを合計しようとしても、同じ仲間のTypeErrorが出ます。CSVやフォームから読み込んだ数字は文字列のことが多いので、計算の前に変換するクセをつけておくと安心です。
原因2 'NoneType' object is not subscriptable¶
このメッセージは、Noneに対して角かっこでアクセスしたときに出ます。subscriptableは、添字でアクセスできるという意味です。
よくあるのは、Noneを返す関数の戻り値をそのまま使ってしまうケースです。代表例が、リストのsort()メソッドです。
scores = [70, 95, 82]
sorted_scores = scores.sort()
print(sorted_scores[0])
sort()はリストそのものを並べ替えるメソッドで、戻り値はNoneです。そのためsorted_scoresにはNoneが入り、[0]でアクセスした瞬間にエラーになります。
並べ替えた新しいリストがほしいなら、sorted()関数を使いましょう。sort()を使う場合は、元のscoresをそのまま参照すれば大丈夫です。
Noneが紛れ込むとドットで属性を使ったときにはAttributeErrorになり、角かっこを使ったときにはTypeErrorになります。原因は同じなので、前回の記事もあわせて読むと理解が深まります。 【関連記事】PythonのAttributeErrorとは?'NoneType' object has no attributeの原因と直し方
原因3 組み込み関数の名前を上書きしてしまった¶
次は、初心者が気づきにくい落とし穴です。'list' object is not callable は、関数ではないものを関数のように呼び出したときに出るメッセージです。
callableは、呼び出せるという意味です。では、なぜlist()が呼び出せなくなるのでしょうか。
list = [1, 2, 3]
letters = list("abc")
1行目でlistという名前の変数を作ったことで、組み込み関数のlist()が隠れてしまいました。2行目のlistは関数ではなくリストを指しているので、呼び出せないのです。
同じことは、print、str、sum、max、input、dictなどでも起こります。print = "こんにちは" と書いてしまうと、その後のprint()はすべて 'str' object is not callable になります。
Jupyterでは再起動するまで直らない¶
この問題がやっかいなのは、Jupyter NotebookやGoogle Colabを使っているときです。変数名を直してセルを実行し直しても、上書きされたlistは残ったままになります。
いったん上書きされた名前は、カーネルを再起動するか、del list で変数を消すまで元に戻りません。変数名には、numbersやitemsのように中身を表す名前をつけるのが確実な予防策です。
私は10年ほどエンジニアとして開発に関わってきましたが、新人研修のサポートでいちばん多く質問されたのがこのエラーでした。Notebookの上のほうのセルで input = 5 と書いていて、30分ほど二人で原因を探したこともあります。
原因4 関数に渡す引数の数や種類が違う¶
関数を呼び出すときの引数の数が合っていないと、これもTypeErrorになります。メッセージには、どの引数が足りないのかまで書かれています。
def greet(name, greeting):
print(f"{greeting}、{name}さん")
greet("田中")
# TypeError: greet() missing 1 required positional argument: 'greeting'
missing 1 required positional argument は、必須の位置引数が1つ足りないという意味です。最後に書かれている 'greeting' が、渡し忘れた引数の名前です。
逆に引数を多く渡しすぎると、takes 2 positional arguments but 3 were given のようなメッセージになります。クラスのメソッドでselfを書き忘れたときにも、この形のエラーが出ることがあります。
引数の型の間違いもTypeErrorになる¶
数だけでなく、組み込み関数に渡す値の型が違う場合もTypeErrorです。たとえば len(5) は、object of type 'int' has no len() というメッセージになります。
len()は文字列やリストのように長さを持つものにしか使えません。数値の桁数を知りたいなら、len(str(5))のように一度文字列に変換してから使いましょう。
原因5 unhashable type: 'list'¶
最後は、辞書や集合を使い始めたころに出会いやすいメッセージです。unhashable type: 'list' は、リストを辞書のキーや集合の要素にしようとしたときに出ます。
辞書のキーや集合の要素には、ハッシュ値を計算できる値しか使えません。公式ドキュメントの用語集でも、リストや辞書のような変更できるコンテナはハッシュ可能ではないと説明されています。
ハッシュ値とは、値をもとに計算される番号のようなものです。中身を書き換えられるリストは番号が変わってしまうので、キーには向いていないのです。
visited = {}
point = [3, 5]
visited[tuple(point)] = True
print(visited)
座標のように複数の値をキーにしたいときは、このようにタプルに変換するのが定番です。タプルは中身を書き換えられないので、要素がすべてハッシュ可能ならキーとして使えます。
変更できる型とできない型の違いは、こちらの記事でくわしく解説しています。 【関連記事】Pythonのミュータブルとイミュータブルとは?変更できる型・できない型の違い
TypeErrorを防ぐための考え方¶
ここまで見てきたように、TypeErrorの原因の多くは、変数の中身が思っていた型と違うことにあります。そこで、日ごろから型を意識する習慣をつけておくと、エラーにぐっと強くなります。
いちばん手軽なのは、エラーが出た行の直前で型を表示してみることです。print(type(price)) と書くだけで、想定と違う型が入っていないかすぐにわかります。
外から受け取った値を扱う関数では、型を確かめてからわかりやすいメッセージを出す方法もあります。次の例は、数値の合計を計算する関数に型チェックを入れたものです。
def total_price(prices):
total = 0
for p in prices:
if not isinstance(p, (int, float)):
raise TypeError(f"数値ではない値が含まれています: {p!r}")
total += p
return total
print(total_price([100, 250, 80]))
try:
total_price([100, "250", 80])
except TypeError as e:
print("エラー:", e)
実行すると、1行目は430と表示され、2つめの呼び出しでは数値ではない値が含まれています: '250' と表示されます。どの値が原因なのかが一目でわかるので、あとから読む人にもやさしいコードになります。
型の確かめ方には、isinstance()とtype()の2種類があります。使い分けのポイントはこちらの記事でまとめています。 【関連記事】Pythonのisinstance()とtype()の違いとは?型チェックの正しい書き方
また、型ヒントを書いてmypyなどの型チェッカーを使うと、実行する前にTypeErrorの芽を見つけられます。実務では、この実行前に型の間違いに気づける仕組みが大きな安心材料になります。
私が以前関わった案件では、APIから返ってくる数値がある日から文字列に変わり、集計バッチがTypeErrorで止まったことがありました。それ以来、外部から受け取るデータは入口で型をそろえることを徹底しています。
まとめ¶
TypeErrorは、値の型がその操作に合っていないことを知らせるエラーです。メッセージには関係する型の名前が書かれているので、まずは型の名前を読むことが解決への第一歩になります。
最後に、この記事で紹介した原因と直し方をもう一度表で振り返っておきましょう。
| 原因 | 直し方 |
|---|---|
| 文字列と数値を混ぜた | int()やstr()で型をそろえる、f-stringを使う |
| Noneに角かっこでアクセスした | Noneを返す関数の戻り値を確かめる |
| 組み込み関数名を上書きした | 変数名を変え、Jupyterならカーネルを再起動する |
| 引数の数が合っていない | メッセージに書かれた引数名を確認する |
| リストをキーにした | タプルに変換する |
エラーが出るたびに落ち込む必要はまったくありません。TypeErrorは、Pythonが型の食い違いを早めに教えてくれている親切なサインです。
ひとつずつ原因を確かめていけば、同じエラーで長く悩むことはだんだん減っていきます。今日の内容が、次にTypeErrorに出会ったときの手がかりになればうれしいです。
ここまでお読みいただきありがとうございました。