このBlogは移転しました。今後は aish.dev を御覧ください。

Python 3.8 の概要 (その8) - Did you mean "=="?

さて、質問です。

a = 1.0
a is 1.0

上記の処理で、a is 1.0 の結果は True となるでしょうか、それとも False となるでしょうか?

True と答えたあなた、不正解です。反省してください。

False と答えたあなた、同じく不正解です。猛省してください。

正解は 「わからない」 です。

Pythonインタープリタを起動して、対話的に実行してみましょう。

>>> a = 1.0
>>> a is 1.0
False

False ですね。a に代入した float オブジェクトと、a is 1.0 で比較している float オブジェクトは、同じ値ですが異なるオブジェクトです。

でも、ちょっと書き換えて、同じ処理を関数の中で実行するとどうでしょう?

>>> def test():
...     a = 1.0
...     return a is 1.0
...
>>> test()
True

True ですね。まったく同じように書いているのに、グローバルに実行する場合と、関数内で実行する場合では、結果が異なっています

このように、定数を is で比較しても、意味のある結果は得られません。 プログラミング初心者が、変数が同じ値かどうか調べるのに is 演算子を使ってしまうことがありますが、これは間違いです。==を使わなくてはなりません。

>>> a = 1.0
>>> a == 1.0
True

だいぶ前に is演算子の不思議で解説しましたが、スクリプト中で 1.0"hello"などの数値や文字列、タプルなどの定数を書いたとき、対応する数値オブジェクトや文字列オブジェクトを新しく作るのか、それとも作成済みのオブジェクトを使い回すのか、言語仕様では決められていません。つまり、1.0 is 1.0 という式の値はやってみなければわからないし、やってみても次にやったときに同じ結果になるとは限らないのです。

ということで、Python3.8 では、このような比較を行うと警告が表示されるようになりました。

>>> a = 1.0
>>> a is 1.0
<stdin>:1: SyntaxWarning: "is" with a literal. Did you mean "=="?
False

「はい? 正気ですか? is ? == の間違いじゃないですか? 定数ですよこれ?」と言われてるわけです。素直に == を使いましょう。

is が使える場合

と書きましたが、is での比較が推奨されている場合もあります。

https://www.python.org/dev/peps/pep-0008/#programming-recommendations では、

Comparisons to singletons like None should always be done with is or is not, never the equality operators.

(None などのシングルトンとの比較は、 == ではなく、常に isis not で行います)

とされています。

上で書いたように、1.0abc などの定数値オブジェクトは、どんなときに新しく作られ、どんなときに作成済みのオブジェクトを使い回すのか、決まっていません。

しかし、例外として、「オブジェクトが常に一つだけ存在する」と言語仕様で決められている値があり、「シングルトン」 と呼ばれます。シングルトンなオブジェクトは、Python開始時に一つだけ作成され、終了するまで削除されずにそのオブジェクトを使いまわします。

このようなシングルトンな値との比較は、同じ値のオブジェクトは一つだけしか存在しないので、== ではなく is を使っても安全です。

シングルトンの代表例として、None があります。NoneNoneType 型のオブジェクトですが、NoneType 型はインスタンスを一つだけしか持たず、None is Noneという式は、常に Trueとなることが保証されています。

ですから、シングルトンな値との比較の場合は、is 演算子で比較しても警告は出力されません。

>>> a = None
>>> a is None
True

シングルトンには、他にも TrueFalse があります。TrueFalsebool 型のオブジェクトですが、どちらも常にひとつだけ存在する事になっています。したがって、この場合も True is TrueFalse is False の結果は常に Trueとなることが保証されていますので、 is で比較しても安心です。

>>> a = True
>>> a is True
True
>>> a is bool('1000')
True

bool 型のオブジェクトは TrueFalse の2つのインスタンスが存在しますので、デザインパターンでいう「シングルトン」とはちょっと違う気がしますが、それはともかくPythonでは TrueFalseis で比較する慣習になっています。

シングルトンにはもう一つ、Ellipsis が存在します。 普通のアプリケーションだとあんまり比較することはなさそうですが、これも is で比較できます。

>>> a = ...
>>> a is ...
True
>>> ... is Ellipsis
True
>>> ...is...
True

ところで、

>>> ...is...is...is...is...is...is...is...is...is...
True

これ、意味は全くありませんが一見Pythonに見えなくて好きです。