- Python
Python None判定はis Noneで|空文字・空リスト・0との違い早見表
PythonでNoneかどうかを判定するときは、x is None(Noneでないことは x is not None)と書きます。 None は「値がない」ことを表すただ1つのオブジェクトで、空文字 ""、空リスト []、0、False とは別のものです。if not x: と書くと、これらがすべて同じ扱いになるため、「Noneだけを見分けたい」ときは is None を使います。
この記事では、None・空文字・空リスト・0・False・NaN を「is None/== None/not x/len(x)==0」で判定した結果を1枚の早見表にまとめ、全パターンの実行結果を載せました。さらに、__eq__ を上書きしたクラスや NumPy 配列で == None が思わぬ結果になる例を再現し、X | None の型ヒント、pandas の NaN/None 判定、関数の引数デフォルトに None を使う定番コードまで、コピーして使える形で解説します。実行環境は Python 3.11.15、NumPy 2.4.4、pandas 3.0.2、mypy 2.3.1 です。
None・空文字・空リスト・0の判定早見表
次のコードで、14種類の値を5通りの方法で判定しました。
# n1.py
values = [
("None", None), ('""', ""), ('" "', " "), ("[]", []), ("{}", {}), ("()", ()),
("set()", set()), ("0", 0), ("0.0", 0.0), ("False", False),
('float("nan")', float("nan")), ('"0"', "0"), ("[None]", [None]), ('"None"', "None"),
]
def safe_len(x):
try:
return len(x) == 0
except TypeError:
return "TypeError"
print(f"{'値':<14}{'is None':<9}{'== None':<9}{'not x':<7}{'bool(x)':<9}{'len(x)==0'}")
for label, v in values:
print(f"{label:<14}{str(v is None):<9}{str(v == None):<9}{str(not v):<7}{str(bool(v)):<9}{safe_len(v)}")
実行結果を表に整えたものが、この記事の早見表です。
| 値 | x is None |
x == None |
not x |
bool(x) |
len(x) == 0 |
|---|---|---|---|---|---|
None |
True | True | True | False | TypeError |
""(空文字) |
False | False | True | False | True |
" "(空白1つ) |
False | False | False | True | False |
[](空リスト) |
False | False | True | False | True |
{}(空辞書) |
False | False | True | False | True |
()(空タプル) |
False | False | True | False | True |
set()(空集合) |
False | False | True | False | True |
0 |
False | False | True | False | TypeError |
0.0 |
False | False | True | False | TypeError |
False |
False | False | True | False | TypeError |
float("nan") |
False | False | False | True | TypeError |
"0"(文字列) |
False | False | False | True | False |
[None] |
False | False | False | True | False |
"None"(文字列) |
False | False | False | True | False |
この表から読み取れる要点は4つです。
| 要点 | 内容 |
|---|---|
is None が True になるのは None だけ |
「値がない」ことだけを見分けたいときはこれ |
not x は「空っぽ・ゼロ」も True |
None・空文字・空のコンテナ・0・False をまとめて扱う |
len(x) == 0 は長さを持つ型だけ |
None や数値に使うと TypeError |
| NaN はどの判定でも「値あり」側 | math.isnan() や pd.isna() で別に調べる |
やりたいこと別の書き方もまとめておきます。
| やりたいこと | 書き方 |
|---|---|
| None かどうか | if x is None: |
| None でないか | if x is not None: |
| 空文字・空リストなどをまとめて「空」と扱う | if not x: |
| None と空文字だけを「未入力」と扱う(0 は値あり) | if x is None or x == "": |
| リストが空か(None は来ない前提) | if not items: |
| None または空リスト | if not items:(0 や False が来ない変数なら) |
| NaN かどうか | math.isnan(x)/pd.isna(x) |
| None または NaN | pd.isna(x)(pandas 利用時) |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python None判定はis Noneで|空文字・空リスト・0との違い早見表」
Noneとは|「値がない」を表すただ1つのオブジェクト
None は NoneType 型のただ1つのインスタンスで、「値がない・未設定・該当なし」を表します。 プログラムのどこで None と書いても、同じ1つのオブジェクトを指します。
# n5.py(抜粋)
print(type(None), None is None)
print(type(None)() is None)
def f():
pass
print(f(), f() is None)
print([1, 2].sort(), {"a": 1}.get("b"))
<class 'NoneType'> True
True
None True
None None
type(None)() で新しく作ろうとしても、返ってくるのは同じ None です。「1つしかない」からこそ、同一性を調べる is で判定できるわけです。
Noneが返ってくる代表的な場面
| 場面 | 例 | 結果 |
|---|---|---|
return のない関数 |
def f(): pass の f() |
None |
| リストをその場で並べ替えるメソッド | [1, 2].sort() |
None |
| 辞書にキーがない | {"a": 1}.get("b") |
None |
| 正規表現が一致しない | re.match(r"\d+", "abc") |
None |
print() の戻り値 |
x = print("a") |
None |
JSON の null |
json.loads("null") |
None |
Noneのまま使うと出るエラー
None と気づかずに操作したときのエラー文を、実際に出して確かめました。
| 操作 | エラー文 |
|---|---|
None.upper() |
AttributeError: 'NoneType' object has no attribute 'upper' |
None + 1 |
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int' |
len(None) |
TypeError: object of type 'NoneType' has no len() |
None[0] |
TypeError: 'NoneType' object is not subscriptable |
for x in None: |
TypeError: 'NoneType' object is not iterable |
sorted([3, None, 1]) |
TypeError: '<' not supported between instances of 'NoneType' and 'int' |
エラー文に 'NoneType' と出てきたら、「どこかの変数が None になっている」という合図です。その変数に値を入れている関数の戻り値をさかのぼって確認し、is None の分岐を入れると解決します。
is Noneと== Noneの違い
2つの演算子は調べているものが違う
| 演算子 | 調べること | 上書きできるか |
|---|---|---|
x is None |
x と None が同じオブジェクトか(同一性) | できない |
x == None |
x と None が等しいとみなせるか(等価性)。x.__eq__(None) の結果 |
クラス側で自由に上書きできる |
普通の値では両方とも同じ結果になります。違いが表に出るのは、== の意味を独自に決めている型を扱うときです。Python のコーディング規約 PEP 8 にも「Comparisons to singletons like None should always be done with is or is not, never the equality operators.(None のようなシングルトンとの比較は常に is か is not で行う)」と書かれています。
再現1:__eq__を上書きしたクラス
# n2.py
class Always:
def __eq__(self, other):
return True
a = Always()
print("a == None :", a == None)
print("a is None :", a is None)
class Money:
def __init__(self, amount):
self.amount = amount
def __eq__(self, other):
return self.amount == other.amount
m = Money(100)
print("m is None :", m is None)
try:
print(m == None)
except AttributeError as e:
print("AttributeError:", e)
a == None : True
a is None : False
m is None : False
AttributeError: 'NoneType' object has no attribute 'amount'
Always は None ではないのに == None が True になりました。Money は、__eq__ の中で相手の amount を読もうとして AttributeError になっています。is None はクラスの実装に左右されず、どちらも正しく False を返します。
再現2:NumPy配列
# n3.py(抜粋)
import numpy as np
arr = np.array([1, 2, 3])
print("arr == None :", arr == None)
print("arr is None :", arr is None)
try:
if arr == None:
print("None です")
except ValueError as e:
print("ValueError:", e)
obj = np.array([1, None, 3], dtype=object)
print("obj == None :", obj == None)
arr == None : [False False False]
arr is None : False
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
obj == None : [False True False]
NumPy 配列の == は要素ごとの比較で、結果は真偽値1つではなく配列です。それを if に渡すと「どの要素で判断するのか決められない」という ValueError になります。配列そのものが None かどうかは arr is None で調べます。
配列には not arr も使えません。空かどうかは size で調べます。
# n3.py(抜粋)
empty = np.array([])
if not empty: ... # ValueError
if not arr: ... # ValueError
ValueError The truth value of an empty array is ambiguous. Use `array.size > 0` to check that an array is not empty.
ValueError The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
「None または空の配列」を1行で扱う定番の形です。
# n11.py(抜粋)
import numpy as np
def total(arr=None):
if arr is None or arr.size == 0:
return 0
return int(arr.sum())
print(total(), total(np.array([])), total(np.array([1, 2, 3])))
0 0 6
直す前と後
| 直す前 | 起きること | 直した後 |
|---|---|---|
if x == None: |
__eq__ を持つ型で結果が変わる |
if x is None: |
if x != None: |
同上 | if x is not None: |
if not x is None: |
動くが読みにくい | if x is not None: |
if arr == None:(NumPy) |
ValueError |
if arr is None: |
if not arr:(NumPy) |
ValueError |
if arr is None or arr.size == 0: |
静的チェックツールもこの書き方を見つけてくれます。次のファイルを pycodestyle 2.15.0 にかけた結果です。
# n9.py
x = None
if x == None:
print("a")
if x != None:
print("b")
if not x is None:
print("c")
n9.py:2:6: E711 comparison to None should be 'if cond is None:'
n9.py:4:6: E711 comparison to None should be 'if cond is not None:'
n9.py:6:4: E714 test for object identity should be 'is not'
isを使うのはNone・True・Falseなどに限る
is が向いているのは、1つしか存在しないオブジェクトとの比較です。数値や文字列のリテラルに is を使うと、Python が警告を出します。
# n10.py
x = 0
print(x is 0, x == 0)
n10.py:2: SyntaxWarning: "is" with a literal. Did you mean "=="?
True True
| 比べる相手 | 使う演算子 |
|---|---|
None |
is / is not |
| 数値、文字列、リストなどの「値」 | == / != |
自作の番兵オブジェクト(後述の _MISSING) |
is / is not |
if not xとis Noneの使い分け
if not xは「偽とみなされる値」すべてに反応する
if x: や if not x: は、bool(x) の結果で分岐します。偽(falsy)とみなされるのは、None・False・数値のゼロ・空のコンテナです。
| 分類 | 偽とみなされる値 |
|---|---|
| 定数 | None、False |
| 数値のゼロ | 0、0.0、0j、Decimal(0)、Fraction(0, 1) |
| 空のシーケンス・コレクション | ""、[]、()、{}、set()、range(0)、b"" |
| 自作クラス | __bool__ が False、または __len__ が 0 を返すもの |
これ以外は真(truthy)です。早見表で確かめたとおり、" "(空白1つ)、"0"、[None]、"None"、float("nan") はすべて真になります。
0を「未指定」と取り違える例
# n6.py(抜粋)
def show(count=None):
if not count:
return "未指定"
return f"{count}件"
print(show(0), show(None), show(3))
def show2(count=None):
if count is None:
return "未指定"
return f"{count}件"
print(show2(0), show2(None), show2(3))
未指定 未指定 3件
0件 未指定 3件
if not count: では、正当な値である 0 まで「未指定」になりました。 is None に直すと 0件 と表示されます。PEP 8 も「if x is not None のつもりで if x と書かないよう注意する」と述べています。
orでデフォルト値を入れるときも同じ
# n8.py(抜粋)
cfg = {"timeout": 0, "retries": None}
print(cfg.get("timeout") or 30) # 0 が 30 に置き換わる
t = cfg.get("timeout")
print(30 if t is None else t) # 0 がそのまま残る
30
0
x or 既定値 は、x が 0 や空文字のときも既定値に置き換わります。 0 や空文字を有効な値として扱う変数では、既定値 if x is None else x と書きます。
どちらを使うかの判断表
| 変数に入りうる値 | 見分けたいこと | 書き方 |
|---|---|---|
| None か、オブジェクト | 値があるか | if x is None: |
| None か、数値(0 を含む) | 未指定か | if x is None: |
| None か、文字列(空文字を区別したい) | 未設定か、空の入力か | if x is None: と if x == "": を分ける |
| None か、文字列(空文字も未入力扱い) | 入力があるか | if not x: |
| リスト(None は来ない) | 要素があるか | if not items: |
| None か、リスト | 処理する要素があるか | if not items:(None と空を同じに扱ってよいとき) |
| 真偽値か None(3状態) | 未回答か | if flag is None: を先に書く |
空のシーケンスの判定について、PEP 8 は if not seq: / if seq: の形を勧めています(if len(seq): より短く読みやすいため)。
値の種類を言葉で返す関数
None・空文字・空のコンテナを区別して扱いたいときの完成形です。
# n11.py(抜粋)
def describe(x):
if x is None:
return "None(値がない)"
if isinstance(x, str) and x.strip() == "":
return "空文字・空白だけ"
if isinstance(x, (list, tuple, dict, set)) and len(x) == 0:
return "空のコンテナ"
return f"値あり: {x!r}"
for v in [None, "", " ", [], {}, 0, False, "0", [None]]:
print(f"{v!r:8} -> {describe(v)}")
None -> None(値がない)
'' -> 空文字・空白だけ
' ' -> 空文字・空白だけ
[] -> 空のコンテナ
{} -> 空のコンテナ
0 -> 値あり: 0
False -> 値あり: False
'0' -> 値あり: '0'
[None] -> 値あり: [None]
is None を最初に判定するのがポイントです。None に対して x.strip() や len(x) を呼ぶとエラーになるため、先に None を取り除いておきます。
NaNはNoneとは別物|判定はisnanで
NaN(Not a Number)は「数値として表せない結果」を表す浮動小数点数の特別な値で、型は float です。 None とは別のもので、判定方法も異なります。
# n5.py(抜粋)
import math
nan = float("nan")
print(nan == nan, nan is nan, nan != nan, math.isnan(nan), bool(nan), nan is None)
print(float("nan") in [float("nan")])
False True True True True False
False
| 性質 | 結果 |
|---|---|
nan == nan |
False(自分自身とも等しくない) |
nan != nan |
True |
bool(nan) |
True(not nan は False) |
nan is None |
False |
math.isnan(nan) |
True |
== では見つけられず、if not x: でも素通りするので、NaN の判定には専用の関数を使います。
| 相手 | 判定方法 |
|---|---|
| Python の float | math.isnan(x) |
| NumPy のスカラー・配列 | np.isnan(x) |
| pandas の値・Series・DataFrame | pd.isna(x)(None も NaN も True) |
None と NaN のどちらも「欠けている」と扱いたい場合の関数です。math.isnan() に文字列などを渡すと TypeError になるため、float かどうかを先に確かめています。
# n11.py(抜粋)
import math
def is_missing(v):
return v is None or (isinstance(v, float) and math.isnan(v))
print([is_missing(v) for v in [None, float("nan"), 0, "", 1.5]])
[True, True, False, False, False]
pandasでのNone・NaN判定
pandas では、欠損値の判定は isna()/notna() に任せるのが基本です。is None や == None が期待どおりに働かない場面を、pandas 3.0.2 で確かめました。
数値の列ではNoneがNaNに変わる
# n4.py(抜粋)
import pandas as pd, numpy as np
s = pd.Series([1.0, None, np.nan, 4.0])
print(s)
print(s.isna().tolist())
print((s == None).tolist())
print([v is None for v in s])
0 1.0
1 NaN
2 NaN
3 4.0
dtype: float64
[False, True, True, False]
[False, False, False, False]
[False, False, False, False]
数値の Series に None を入れると、格納の時点で NaN に変換されます。 そのため == None も、要素を取り出しての is None も、すべて False でした。isna() だけが2件の欠損を見つけています。
整数だけのつもりで pd.Series([1, None, 3]) を作ると、型は float64、中身は [1.0, nan, 3.0] になります。整数のまま欠損を持たせたいときは dtype="Int64" を指定します(中身は [1, <NA>, 3]、isna() は [False, True, False])。
文字列の列・object型の列
# n4.py(抜粋)
o = pd.Series(["a", None, np.nan, ""])
print(o.dtype, o.isna().tolist(), [v is None for v in o])
ob = pd.Series(["a", None, np.nan, ""], dtype=object)
print(ob.tolist(), [v is None for v in ob], ob.isna().tolist())
str [False, True, True, False] [False, False, False, False]
['a', None, nan, ''] [False, True, False, False] [False, True, True, False]
pandas 3.0.2 では文字列の列の型が str になり、None は NaN として保持されました(is None はすべて False)。dtype=object を明示したときだけ None がそのまま残ります。列の型によって None の残り方が変わるので、isna() に統一しておくと型を気にせずに済みます。 なお、空文字 "" は欠損として扱われません。
pd.isnaが欠損とみなす値
| 値 | pd.isna(値) |
|---|---|
None |
True |
np.nan / float("nan") |
True |
pd.NA |
True |
pd.NaT(日時の欠損) |
True |
""(空文字) |
False |
0 |
False |
pd.NA は if pd.NA: のように真偽値として使うと TypeError: boolean value of NA is ambiguous になり、pd.NA == None や pd.NA is None はどちらも False でした。ここでも pd.isna() を使います。
DataFrameでよく使う操作
# n4.py(抜粋)
df = pd.DataFrame({"name": ["A", "B", None], "price": [100, None, 300]})
print(df.isna().sum()) # 列ごとの欠損数
print(df["price"].fillna(0).tolist()) # 欠損を 0 で埋める
print(df.dropna()) # 欠損のある行を除く
print(df[df["price"].isna()]) # price が欠損の行だけ
name 1
price 1
dtype: int64
[100.0, 0.0, 300.0]
name price
0 A 100.0
name price
1 B NaN
| やりたいこと | 書き方 |
|---|---|
| 欠損かどうか(要素ごと) | df.isna() / s.isna() |
| 欠損でないか | df.notna() / s.notna() |
| 列ごとの欠損数 | df.isna().sum() |
| 欠損のある行を除く | df.dropna() |
| 欠損を値で埋める | s.fillna(0) |
| 欠損の行だけ取り出す | df[df["price"].isna()] |
| 1つの値が欠損か | pd.isna(value) |
行から取り出した値を判定するとき
row = df.iloc[1]
print(repr(row["price"]), row["price"] is None, pd.isna(row["price"]))
np.float64(nan) False True
取り出した値は None ではなく NaN なので、is None では見つかりません。pandas から取り出した値は pd.isna() で判定します。
JSON に書き出す前など、NaN を Python の None に戻したいときは次の形が使えます。
recs = df.astype(object).where(df.notna(), None).to_dict("records")
print(recs)
[{'name': 'A', 'price': 100.0}, {'name': 'B', 'price': None}, {'name': None, 'price': 300.0}]
型ヒントでNoneを表す|X | NoneとOptional
「None かもしれない値」は、型ヒントで X | None と書きます(Python 3.10 以降)。それより前から使える Optional[X] と同じ意味です。
# n8.py(抜粋)
from typing import Optional
print(Optional[str] == (str | None))
print(str | None)
print(Optional[str])
True
str | None
typing.Optional[str]
| 書き方 | 使える版 | 意味 |
|---|---|---|
str | None |
Python 3.10 以降 | str または None |
Optional[str] |
それ以前から(from typing import Optional) |
同じ |
Union[str, None] |
それ以前から | 同じ |
Optional は「引数を省略できる」という意味ではなく、「None が入りうる」という意味です。省略できるかどうかは、デフォルト値(= None)の有無で決まります。
型チェッカーがNoneの見落としを教えてくれる
# n7.py
def find_user(user_id: int) -> str | None:
users = {1: "sato", 2: "suzuki"}
return users.get(user_id)
name = find_user(3)
print(name.upper())
実行すると、該当者がいないため None に対して upper() を呼ぶことになります。
AttributeError: 'NoneType' object has no attribute 'upper'
同じファイルを mypy 2.3.1 にかけると、実行する前にこの箇所を指摘してくれます。
n7.py:11: error: Item "None" of "str | None" has no attribute "upper" [union-attr]
Found 1 error in 1 file (checked 1 source file)
is Noneで分岐すると型が絞り込まれる
# n8.py(抜粋)
def find_user(user_id: int) -> str | None:
return {1: "sato", 2: "suzuki"}.get(user_id)
name = find_user(3)
if name is None:
print("見つかりませんでした")
else:
print(name.upper()) # ここでは name は str と推論される
if (name := find_user(1)) is not None:
print(name.upper())
見つかりませんでした
SATO
is None で分けたあとの else 側では、型チェッカーが「ここでは str」と判断するため、指摘は出なくなります。is None は実行時の安全と、型チェックの通過を同時に満たす書き方です。:=(代入式)を使うと、取得と判定を1行にまとめられます。
引数のデフォルトにNoneを使う定番コード
リストや辞書をデフォルト値にするとどうなるか
# n6.py(抜粋)
def add_bad(item, bucket=[]):
bucket.append(item)
return bucket
print(add_bad("a"))
print(add_bad("b"))
['a']
['a', 'b']
2回目の呼び出しに、1回目の "a" が残っています。デフォルト値は関数を定義したときに1度だけ作られ、呼び出しのたびに同じリストが使い回されるためです。
Noneを目印にして、関数の中で作る
# n6.py(抜粋)
def add_good(item, bucket=None):
if bucket is None:
bucket = []
bucket.append(item)
return bucket
print(add_good("a"))
print(add_good("b"))
['a']
['b']
「デフォルトは None、関数の先頭で if x is None: を見て新しく作る」が定番の形です。型ヒントを付けるなら bucket: list[str] | None = None と書きます。
bucket or [] と書くと変わること
# n6.py(抜粋)
def add_or(item, bucket=None):
bucket = bucket or []
bucket.append(item)
return bucket
shared = []
add_or("x", shared)
print("shared:", shared)
add_good("x", shared)
print("shared:", shared)
shared: []
shared: ['x']
bucket or [] は、呼び出し側が渡した空リストも「偽」とみなして新しいリストに置き換えます。 そのため shared には何も追加されませんでした。渡されたリストに追記したい関数では is None で判定します。
「Noneを渡した」と「省略した」を区別する
None 自体に意味がある引数(「値を消す」など)では、専用の番兵オブジェクトをデフォルトにします。
# n6.py(抜粋)
_MISSING = object()
def update(name, nickname=_MISSING):
if nickname is _MISSING:
return f"{name}: ニックネームは変更しない"
if nickname is None:
return f"{name}: ニックネームを消す"
return f"{name}: ニックネームを {nickname} にする"
print(update("sato"))
print(update("sato", None))
print(update("sato", "さとう"))
sato: ニックネームは変更しない
sato: ニックネームを消す
sato: ニックネームを さとう にする
object() で作った値は他のどの値とも同一にならないので、is で確実に見分けられます。
dataclassでの書き方
# n11.py(抜粋)
from dataclasses import dataclass, field
@dataclass
class Task:
title: str
due: str | None = None
tags: list[str] = field(default_factory=list)
t = Task("掃除")
print(t, t.due is None)
Task(title='掃除', due=None, tags=[]) True
「未設定がありうる項目」は X | None = None、リストのように毎回新しく作りたい項目は field(default_factory=list) を使います。
リスト・辞書の中のNoneを扱うコード集
# n8.py(抜粋)
items = [3, None, 0, "", None, "a"]
print([x for x in items if x is not None]) # None だけ除く
print([x for x in items if x]) # 偽の値をすべて除く
print(list(filter(None, items))) # 同上
print(items.count(None), None in items)
print(any(x is None for x in items), all(x is not None for x in items))
print(sorted([3, None, 1], key=lambda x: (x is None, x)))
[3, 0, '', 'a']
[3, 'a']
[3, 'a']
2 True
True False
[1, 3, None]
| やりたいこと | 書き方 | 注意点 |
|---|---|---|
| None だけ取り除く | [x for x in items if x is not None] |
0 や空文字は残る |
| 偽の値をまとめて取り除く | [x for x in items if x] / filter(None, items) |
0 や空文字も消える |
| None の個数 | items.count(None) |
|
| None が含まれるか | None in items / any(x is None for x in items) |
|
| None を最後に回して並べ替え | sorted(items, key=lambda x: (x is None, x)) |
None 以外は互いに比較できる型にそろえる |
辞書では、「キーがない」と「キーはあるが値が None」を分けて考えます。
# n11.py(抜粋)
d = {"a": None}
print("a" in d, d.get("a") is None, d.get("b") is None, "b" in d)
True True True False
d.get(key) is None はどちらの場合も True になります。キーの有無を知りたいときは key in d を使います。また、d.get("retries", "default") のように既定値を渡しても、キーが存在して値が None の場合は None が返ります(実行して確認しました)。
match 文でも None を直接書けます。
match value:
case None:
print("値がない")
case _:
print("値あり")
Noneと文字列の"None"を取り違えないために
# n5.py(抜粋)
print(str(None), repr(None), f"{None}", "None" == None, str(None) == "None")
None None None False True
str(None) や f-string に None を入れると、文字列の "None" になります。 画面やファイルに None と出ていても、それが None なのか文字列なのかは見た目では区別できません。確かめるときは repr(x) や type(x) を使います。
| 値 | repr() |
is None |
bool() |
|---|---|---|---|
None |
None |
True | False |
"None" |
'None' |
False | True |
"null" |
'null' |
False | True |
CSV やフォームから読んだ "None"、"null"、"" を None として扱いたい場合は、読み込みの入口で明示的に変換しておくと、その後の判定を is None に統一できます。
def to_none(text: str) -> str | None:
return None if text.strip() in ("", "None", "null") else text
チェックリスト
- [ ] None との比較は
is None/is not Noneで書いた - [ ] 0 や空文字が有効な値になりうる変数に
if not x:やx or 既定値を使っていない - [ ] None かもしれない値は、
len()やメソッド呼び出しの前にis Noneで分岐した - [ ] 戻り値が None になりうる関数に
X | Noneの型ヒントを付けた - [ ] リスト・辞書のデフォルト引数は
Noneにして関数内で作った - [ ] NaN の判定に
==ではなくmath.isnan()/pd.isna()を使った - [ ] NumPy 配列・pandas の値に
== Noneやnot xを使っていない
よくある質問
Q. is None と == None はどちらが速いですか?
A. is はオブジェクトが同じかどうかを見るだけで、== は __eq__ の呼び出しを伴います。速度よりも大切なのは、is None ならクラスの __eq__ 実装に左右されず、常に正しい結果になる点です。
Q. if x: と if x is not None: はどう違いますか?
A. if x: は None のほか、0・空文字・空リスト・False でも偽になります。if x is not None: は None のときだけ偽です。0 や空文字を「値がある」と扱いたい変数では後者を使います。
Q. 空文字と None はどう使い分けますか?
A. 「まだ設定されていない・該当がない」は None、「入力されたが中身が空だった」は空文字、と分けると意味がはっきりします。区別する必要がない場合は、入口でどちらかに統一してから if not x: で判定すると簡潔です。
Q. pandas の DataFrame で None を探すには?
A. df.isna() を使います。数値の列では None が NaN に変換されて格納されるため、== None や is None では見つかりません。isna() は None・NaN・pd.NA・pd.NaT をまとめて欠損として扱います。
まとめ
- None の判定は
x is None/x is not None。None はただ1つのオブジェクトなので同一性で調べる == Noneは__eq__次第で結果が変わる。NumPy 配列では要素ごとの比較になり、ifに渡すとValueErrorif not x:は None・空文字・空リスト・0・False をまとめて偽と扱う。0 や空文字が有効な値ならis None- NaN は None とは別物で、
bool(nan)は True。判定はmath.isnan()/pd.isna() - pandas の数値列では None が NaN に変わる。欠損の判定は
isna()に統一する - 型ヒントは
X | None。is Noneで分岐すると型チェッカーが型を絞り込む - 変更可能なデフォルト引数は
Noneにして関数内で作る。None に意味があるときは番兵オブジェクトを使う
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Python None判定はis Noneで|空文字・空リスト・0との違い早見表」
株式会社RJCでは、PythonのNone判定のような基礎を仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。