Tech カテゴリのイメージ

AttributeError: 'NoneType' object has no attribute の原因と直し方[Python]

Pythonで最も遭遇率の高いエラーのひとつがこれになる。

AttributeError: 'NoneType' object has no attribute 'append'

属性名の部分はsplitだったりgroupだったりstripだったりと変わるが、原因はどれも同じで、Noneに対して何かをしようとしている。

Noneは値がないことを表す特別なオブジェクトで、文字列やリストのようなメソッドを持っていない。だから問題は「なぜそこがNoneになったのか」を突き止めることになる。

1. その場で書き換える関数の戻り値を受け取っている

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

lst = [3, 1, 2]
lst = lst.sort() # sort() は None を返す
lst.append(4) # AttributeError

sortはリスト自身を並べ替えて、戻り値としてはNoneを返す。同じ性質を持つメソッドは多い。

list.sort() list.append() list.reverse() list.extend()
dict.update()
set.add() set.discard()

いずれも元のオブジェクトを書き換えるだけで、結果を返さない。代入しないのが正しい使い方になる。

lst.sort() # これでlstが並び替わる

新しいリストが欲しい場合は、元を変えない関数のほうを使う。

new = sorted(lst) # sorted() は新しいリストを返す

2. 正規表現がマッチしなかった

re.searchre.matchは、見つからなければNoneを返す。

m = re.search(r"\d+", text)
print(m.group()) # マッチしないと AttributeError

見つからない場合を必ず書く。

m = re.search(r"\d+", text)
if m:
print(m.group())
else:
print("見つからない")

既定値が欲しいだけなら、こう書ける。

num = m.group() if m else "0"

3. 辞書のgetが既定値を返した

dict.getはキーがないときNoneを返す。

name = data.get("name")
print(name.upper()) # キーが無いと AttributeError

既定値を明示すれば防げる。

name = data.get("name", "")
print(name.upper())

キーが必ずあるべきならdata["name"]と書いたほうがいい。無いときにKeyErrorで止まるので、原因が分かりやすくなる。

4. 関数がreturnを書き忘れている

自作の関数で、条件によってreturnに到達しないケースがある。

def find_user(users, uid):
for u in users:
if u["id"] == uid:
return u
# 見つからないとき、暗黙のうちに None が返る
user = find_user(users, 5)
print(user["name"]) # AttributeError や TypeError

Pythonの関数はreturnがないと自動的にNoneを返す。見つからない場合の扱いを呼び出し側で書くか、例外を投げるかを決めておく。

user = find_user(users, 5)
if user is None:
raise ValueError(f"ユーザー {uid} が見つかりません")

5. ライブラリの検索系メソッド

BeautifulSoupのfindなど、見つからないとNoneを返すものは多い。

title = soup.find("h1").text # h1が無いと AttributeError
h1 = soup.find("h1")
title = h1.text if h1 else ""

pandasのdf.queryが空になった場合や、os.environ.getなども同じ構造になる。

原因の特定を早くする

どこでNoneになったかが分からないときは、直前で確認してしまうのが早い。

print(type(obj), repr(obj))
obj.something()

型を出せばNoneTypeかどうかが一目で分かる。連鎖している式では、途中で切って変数に入れると特定しやすくなる。

a = get_data()
print(repr(a))
b = a.parse()

なおNoneかどうかの判定は== Noneではなくis Noneを使う。Noneはひとつしか存在しないので、同一性で比べるのが正しい。

📘 None の扱いのような基礎を一度まとめて押さえるなら、独習Python

まとめ

Noneにはメソッドが無いので、原因は「なぜそこがNoneになったか」に尽きる
sort append updateなど、その場で書き換える系の戻り値はNone。代入してはいけない
re.searchdict.getsoup.findは見つからないとNoneを返す。必ず分岐を書く
・自作関数はreturnに到達しない経路があると暗黙にNoneを返す
・判定はis None。原因が読めないときは直前でtype()repr()を出す

※本記事にはアフィリエイト広告を含みます。