[Pythonクイズ]オブジェクトが存在するかどうかを調べるよい方法を知っていますか?Pythonステップアップクイズ

あるオブジェクトとNoneを比較するときって、どんなやり方をしていますか? それって普段は問題なくても実はダメなやり方じゃないですか? ちょっと確認してみましょ。

» 2025年10月14日 05時00分 公開
[かわさきしんじDeep Insider編集部]
「Pythonステップアップクイズ」のインデックス

連載目次

どこかPython風でないところがあるよ? どこかPython風でないところがあるよ?

【問題】

 以下は変数nの値がNoneかどうかを判定するコードである。このコードは問題なく動作するが、Python風ではない。どこを修正すればよいだろう。

n = None

if n == None:
    print('n == None')
else:
    print('n != None')

どこかPython風でないところがあるよ?

Noneて何て読む?

 今回はコードもちょっとシンプルでChatGPT/Claude/Geminiに聞くようなことも思い付かなかったので、「Noneて何て読む?」と聞いてみることにしました。ちょっとした目的があってこんなことを聞いてみたのですが、そこに気が付いたLLMは1つもありませんでした。さて、その目的とは何でしょう。え? 画像がない? そうです。今回はないんです。なんかよいインスピレーションが湧かなかったんです。ごめんなさい。



かわさき

 どうもHPかわさきです。

 Python 3.14.0が正式リリースされましたねー。2025年10月7日の予定だったので、いつリリースになるかをジッと待っていたのですが、日本時間では10月8日に日付が変わる少し前くらいだったんじゃないかな。21時にはまだリリースされていなかったような気がします。

 Python 3.14.0の正式リリースに合わせて、Deep Insiderでは「Python祭り」を自称して、10月中はPython 3.14の新機能を紹介する記事をちょっと多めに投下していく予定です。その手始めに『「Python 3.14」で公式サポートに移行したフリースレッド版Pythonとは? その進化の道筋』を公開しています。興味のある方はぜひ。


【答え】

 正解のコード例を以下に示します。

n = None

if n is None:
    print('n is None')
else:
    print('n is not None')

ある値がNoneかどうかを比較するにはis演算子を使うことが推奨されている

 ==演算子を使うのではなく、is演算子を使って変数nとNoneとを比較することが、Pythonでは推奨されています。2つのprint関数呼び出しではこれに対応してメッセージも変更していますが、これはあくまでおまけの変更です。

ある値がNoneかどうかを比較するにはis演算子を使うことが推奨されている ある値がNoneかどうかを比較するにはis演算子を使うことが推奨されている

 では、is演算子を使う理由とは何でしょう。

【解説】

 Pythonに限らず、多くのプログラミング言語では2つのオブジェクトの値を比較する際に次に示す2種類のやり方があります。

  • 同値性の比較:2つのオブジェクトの値が同じかどうかを比較する
  • 同一性の比較:2つのオブジェクトが同一かどうかを比較する

 簡単な例でこれらの違いを見てみましょう。以下は2つの変数s0とs1に文字列'abc'を代入するコードです。

s0 = 'abc'
s1 = ''.join(['a', 'b', 'c'])

print(id(s0))  # 4553174368など
print(id(s1))  # 4559210032など

s0とs1は異なるオブジェクト

 このコードを実行すると、s0とs1には別々のオブジェクトが代入されます。s0にはコードに記述された文字列がそのまま代入されるのに対して、s1ではリストの全要素を見て、それらを結合して文字列にまとめるのに必要なメモリを確保して、そこに個々の文字列要素を埋め込んで……のような処理が行われた結果が代入されるからです(ちなみに、ここで「s2 = 'abc'」とすると、多くの場合、s0とs2は同じオブジェクトを指すようになります)。

 下の2行で呼び出しているid関数は引数に指定したオブジェクトが位置するメモリアドレスを返します(CPythonの実装)。今述べたような動作をすることから、それぞれのオブジェクトが位置するメモリアドレスは異なるものになります(読者がこのコードを実行するとid関数の戻り値は上のコメントとは異なるものになるでしょうが、2つの戻り値が異なることに変わりはないはずです)。

 そして、2つのid関数呼び出しの戻り値が異なっていることから、これら2つのオブジェクトが別モノであることが分かります。が、その値はどちらも'abc'という文字列です。よって、これらのオブジェクトの値が同一かどうかを知りたいのであれば、==演算子を使います。

print(s0 == s1)  # True

s0とs1の値は同じ

 しかし、2つのオブジェクトは異なるものです。よって、is演算子を使うとそれらが別モノであるかどうかを調べられます。

print(s0 is s1)  # False

s0とs1は別々のオブジェクトを参照している

 ここまで話したことが大前提です。もう1つ重要なことがあります。それはNoneという値はPythonでは「シングルトン」と呼ばれ、「そのコードの実行時にただ一つだけ存在する」もう少し詳しく書くと「Python処理系の実行時にそのプロセス内で、NoneType型の唯一のインスタンスとして存在する」ということです。'abc'という文字列は上でも見たように、複数存在することが許されていますが、Noneはそうではないということです。

 そして、このような値(シングルトン)と比較するときには、is演算子(もしくはis not演算子)を使うことが強く推奨されているのです(PEP 8の『Programming Recommendations』でもそう述べられています)。

 あ、さらにもう1つ重要なことがありました。それは==演算子による比較は、==演算子の左辺に置かれたオブジェクトの__eq__特殊メソッドを呼び出すということです。つまり、2つのオブジェクトの値が等しいかどうかを判定する「s0 == s1」という比較は次のコードと等しくなります。

s0.__eq__(s1)

==演算子の比較では文字列の__eq__特殊メソッドが呼び出される

 では、次のようなクラスがあったとしましょう。

class AlwaysEqual:
    def __eq__(self, other):
        return True

==演算子で比較すると常にTrueを返すクラス

 このようなクラスがあったとして、そのオブジェクトとNoneを比較することを考えてみます。

ae = AlwaysEqual()

print(ae == None# True

==演算子は常にTrueを返すので、AlwaysEqualオブジェクトがNoneと等しくなってしまう

 このように==演算子の振る舞い(__eq__特殊メソッド)は上書きできるので、==演算子によるNoneとの比較はすべきではないということです(逆に、is演算子の振る舞いはオブジェクトのメモリアドレスを比較すると決まっていて、これを変更することはできません)。

 上の例は極端ですが、実際に==演算子の振る舞いを上書きした結果、オブジェクトとNoneの比較がプログラマーの意図とは異なるかもしれないという状況は起こります。例えば、以下のコードがそうです。

import numpy as np

a = np.array([1, 2, 3, None])
print(a == None)

NumPyの多次元配列とNoneを比較するとき、何が起こるか?

 このコードでは変数aには多次元配列が代入されているので、Noneではありません。ということは、「a == None」という比較はFalseがその値になりそうです。でも、実行結果は以下の通りです。

NumPyの多次元配列では==演算子はその各要素と比較対象の値が等しいかどうかを判定し、その結果が多次元配列として戻される NumPyの多次元配列では==演算子はその各要素と比較対象の値が等しいかどうかを判定し、その結果が多次元配列として戻される

 NumPyの多次元配列では==演算子による比較が上書きされ、配列の各要素と比較対象の値が等しいかどうかを判定した結果が多次元配列として戻されます。そのため、オブジェクトが存在しないかどうかを判定しようと思って「a == None」と書くと、想定外の結果となるかもしれません。

 こんなこともあるので、オブジェクトが存在するかどうか(存在しないかどうか)を調べるときにはis演算子(またはis not演算子)を使うことが推奨されているのです。


かわさき

 さて「Noneて何て読む?」と聞いた結果を発表します。

  • ChatGPT:「ナン」または「ノーン」
  • Gemini:「ノン」または「ナニ」
  • Claude:「ナン」または「ノン」

 こんなことを聞いた狙いは『これは「何」と「ナン」を掛けたダジャレですか?』というツッコミを受けることでした。が、そこに気付いたLLMは皆無(がっくり)。というか、Geminiさんは発音まで本場っぽいのかどうなのか、「ナン」とすら答えてくれなかったのでした。後から「ダジャレって分かった?」と聞いたら、皆さん納得はしてくれましたけどね。筆者もまだまだ修行が必要です(ナンの?)。


「Pythonステップアップクイズ」のインデックス

Pythonステップアップクイズ

Copyright© Digital Advantage Corp. All Rights Reserved.

アイティメディアからのお知らせ

スポンサーからのお知らせPR

注目のテーマ

4AI by @IT - AIを作り、動かし、守り、生かす
Microsoft & Windows最前線2025
AI for エンジニアリング
ローコード/ノーコード セントラル by @IT - ITエンジニアがビジネスの中心で活躍する組織へ
Cloud Native Central by @IT - スケーラブルな能力を組織に
システム開発ノウハウ 【発注ナビ】PR
あなたにおすすめの記事PR

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。