できない.dev

Python で「'NoneType' object has no attribute」が解消できない

AttributeError の対象が NoneType の場合、メソッドの戻り値や辞書の get、ライブラリ API が想定外に None を返している。
値が None である可能性を設計で潰すか、is None で早期判定してから属性アクセスする。

#python#attributeerror#nonetype#exception#type-hint

公開:

要約

'NoneType' object has no attribute 'xxx' は、None に対して属性アクセスした ことを示す。
直前のメソッド呼び出しや関数の戻り値が想定と違って None になっていないかを最初に疑う。is None で早期判定するか、そもそも None を返さない型設計にする。

よくある原因

  1. インプレース操作の戻り値を代入: lst = lst.append(x)s = s.sort()None を返す。
    Python のミュータブル系メソッドの典型的な罠。
  2. 既定値なしの get: os.environ.get("X")re.match(pat, s) は値が無い・マッチしないと None
    続けて .group() などを呼ぶと例外。
  3. 関数の return 忘れ: def f(): result = compute() のように return を書き忘れていると、呼び出し側で f().attr が落ちる。
  4. API の None 返却: Django ORM qs.first()requests のオプショナル属性、SQLAlchemy の session.get() などは見つからないと None

解決策

1. インプレースと関数型を見分ける

# NG: append は None を返す
items = items.append(x)
 
# OK: 戻り値を使わない or 関数型版を使う
items.append(x)
items = items + [x]

2. is None で早期判定

m = re.match(r"(\d+)", s)
if m is None:
    return 0
return int(m.group(1))

公式ドキュメント のとおり、None に属性は無い前提で書く。

3. typing.Optional と型チェッカ

from typing import Optional
 
def find_user(uid: int) -> Optional["User"]:
    ...
 
user = find_user(1)
if user is None:
    raise LookupError
user.name   # ここでは User 型に絞られている

mypy / pyright を導入すれば、typing.Optional の取り扱い漏れを CI で潰せる。

4. 既定値や raise する版を選ぶ

env = os.environ.get("KEY", "")    # 既定値
env = os.environ["KEY"]            # 無ければ KeyError
user = User.objects.get(pk=1)      # 無ければ DoesNotExist

「無いときに静かに None で返す API」と「raise する API」を場面で使い分けると、後段で属性アクセスする側の不安が消える。

実行例

実際に上記の手順を python:3.12 環境で動かすと、items = items.append(x)None を返して続く .pop()AttributeError: 'NoneType' object has no attribute 'pop' が発生し(終了コード 1)、各解消策を適用した場合はそれぞれ期待どおりの出力が得られることを確認できる。

== versions ==
PRETTY_NAME="Debian GNU/Linux 13 (trixie)"
Python 3.12.13
git version 2.47.3
== run ==
--- python bad.py (AttributeError: NoneType を想定)---
Traceback (most recent call last):
  File "/tmp/tmp.UiUec5I71q/bad.py", line 4, in <module>
    print(items.pop())
          ^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'pop'
終了コード: 1
--- 解消1: インプレースは戻り値を捨て、変数はそのまま使う ---
正しい結果: [1, 2, 3, 4]
--- 解消2: re.match の None を is None で早期判定 ---
'123-abc': matched '123'
'no digits here': マッチなし
--- 参考: return 忘れの関数は暗黙に None を返す ---
compute の戻り値: None
AttributeError: 'NoneType' object has no attribute 'bit_length'

— 2026-06-03 時点の出力

この記事は役立ちましたか?