エラーの内容
Pythonスクリプトが次のようなエラーでクラッシュします:
AttributeError: 'NoneType' object has no attribute 'split'
has no attribute の後に続くメソッド名は状況によって異なります — 'lower'、'strip'、'replace'、あるいは他の文字列メソッドの場合もあります。しかし原因は常に同じです:コードのどこかで、変数が期待していた文字列(またはリスト、オブジェクト)ではなく None を保持しています。
根本原因
Pythonにおける None は独自の特殊な型 NoneType です。文字列メソッドもリストメソッドも、何も持っていません。.split() を呼び出すと、Pythonは何が問題だったかを正確に教えてくれます。
ほぼ10回中9回は、次のいずれかが原因です:
return文が抜けているために関数が暗黙的にNoneを返している- 変数が一度も代入されていない、または明示的に
Noneに設定されている - 正規表現のマッチや辞書の検索で何も見つからず
Noneが返された - APIレスポンスやファイル読み込みの結果が空だった
クイック診断
まず、どの変数 が None なのかを特定します。クラッシュしている行の直前に print() を追加してみましょう:
# クラッシュしている行:
result = user_input.split(",")
# この行をその上に追加する:
print(type(user_input), repr(user_input))
出力に <class 'NoneType'> None が表示されたら、それが問題の変数です。その変数がその値を持つようになった経緯をコードで遡りましょう — 修正はほぼ必ず一つ前のステップにあります。
修正1 — 値を提供している関数を確認する
return 文の書き忘れが最大の原因です。条件に当たったものの何も返すのを忘れた関数は、こっそり None を返します。
# 問題あり — 暗黙的にNoneを返す
def get_username(data):
if data:
username = data["name"]
# returnを忘れている!
name = get_username({"name": "alice"})
parts = name.split(" ") # AttributeError
# 修正済み
def get_username(data):
if data:
return data["name"]
return "" # または例外を発生させる
name = get_username({"name": "alice"})
parts = name.split(" ") # 正常動作: ['alice']
関数のすべての分岐を確認してください。return なしで終了できる分岐があれば、Pythonは黙って None を補完します。
修正2 — Noneチェックでガードする
None が有効な結果となる場合もあります — オプションフィールド、欠落した設定キー、「見つからない」レスポンスなどです。メソッドを呼び出す前にガード処理を入れましょう:
value = some_function()
# 方法A:明示的なチェック
if value is not None:
parts = value.split(",")
else:
parts = []
# 方法B:ワンライナーのフォールバック
parts = (value or "").split(",")
方法Bは簡潔です。ただし注意が必要です — or "" は None だけでなく、空文字列や 0 でも発動します。その区別が重要な場合は value is not None を使いましょう。
修正3 — 辞書と正規表現の検索を修正する
何も見つからないときに暗黙的に None を返すPython組み込みが2つあります:dict.get() と re.search() です。
# 辞書 — デフォルト値を使う
data = {"city": "Hanoi"}
country = data.get("country") # Noneを返す
parts = country.split("-") # AttributeError
# 修正:フォールバックを指定する
country = data.get("country", "") # NoneではなくStringを返す
parts = country.split("-") # 正常動作: ['']
import re
text = "hello world"
match = re.search(r"(\d+)", text) # 数字なし — Noneを返す
result = match.group(1) # AttributeError
# 修正:使用前にチェックする
if match:
result = match.group(1)
else:
result = None
修正4 — 外部ソースからのNoneを処理する
HTTP API、データベース、ファイル読み込みにはすべて共通点があります:何も返さないことがあるのです。JSONレスポンスにフィールドが存在しない場合もあります。データベースの行が存在しない場合もあります。外部データが常にクリーンだと思い込まないようにしましょう。
import requests
response = requests.get("https://api.example.com/user/1")
data = response.json()
# リスクあり
name = data.get("name")
first, last = name.split(" ") # nameがNoneの場合はAttributeError
# 安全
name = data.get("name") or ""
if " " in name:
first, last = name.split(" ", 1)
else:
first, last = name, ""
修正5 — 型アノテーションと早期バリデーションを使う
大規模なプロジェクトでは、None が3〜4回の関数呼び出しを経た後、ロジックの深い場所でクラッシュすることがあります。これはデバッグが非常に困難です。解決策はエントリポイントでバリデーションを行うことです。
def process_input(text: str) -> list[str]:
if not isinstance(text, str):
raise TypeError(f"str型を期待しましたが、{type(text).__name__}型が渡されました")
return text.split(",")
# これにより、後から混乱するAttributeErrorではなく、すぐに明確なTypeErrorが発生する
process_input(None)
2行目で発生する明確な TypeError: str型を期待しましたが、NoneType型が渡されました は、別モジュールの87行目に埋もれた AttributeError よりはるかに分かりやすいです。
修正の確認
PythonのREPLまたはテストスクリプトで簡単に動作確認します:
value = None
result = (value or "").split(",")
print(result) # 期待値: ['']
value = "a,b,c"
result = (value or "").split(",")
print(result) # 期待値: ['a', 'b', 'c']
どちらもエラーなく実行されるはずです。pytest を使っている場合は、None ケースの明示的なテストを追加しましょう:
def test_handles_none_input():
assert process_csv(None) == []
assert process_csv("a,b") == ["a", "b"]
予防チェックリスト
- 値を返す関数はすべての分岐で明示的な
returnが必要です — 例外なし - キーが存在しない可能性がある場合は
dict[key]の代わりにdict.get(key, default)を使う re.search()/re.match()の結果は.group()を呼ぶ前に必ず確認する- 外部データ(API、DB、ユーザー入力)はロジックの深部ではなく、エントリポイントでバリデーションを行う
mypyまたはpyrightを実行する — コードが実行される前にOptional[str]の誤用を検出してくれる

