1. ホーム
  2. python

[解決済み] if None.__eq__("a")` はなぜ True と評価されるように見えるのでしょうか(完全ではありませんが)?

2022-05-09 22:07:29

質問

Python 3.7で以下の文を実行すると、(私のテストでは)次のように表示されます。 b :

if None.__eq__("a"):
    print("b")

しかし None.__eq__("a") は、次のように評価されます。 NotImplemented .

当然です。 "a".__eq__("a") は、次のように評価されます。 True であり、かつ "b".__eq__("a") は、次のように評価されます。 False .

私は最初、ある関数の戻り値をテストしているときにこれを発見したのですが、2番目のケースでは何も返しませんでした -- つまり、この関数は None .

どうなってるんだ?

解決方法は?

これは、なぜ __dunder__ メソッドを直接使用すべきではありません。なぜなら、同等の演算子の代わりとして適切でないことが多いからです。 == 演算子の代わりに、等値比較や、この特殊なケースで None を使用します。 is (詳しくは解答の一番下まで読み飛ばしてください)。

あなたが行ったのは

None.__eq__('a')
# NotImplemented

を返します。 NotImplemented というのは、比較される型が異なるからです。別の例として、型が異なる2つのオブジェクトをこの方法で比較する場合を考えてみましょう。 1'a' . すること (1).__eq__('a') も正しくありません。 NotImplemented . この 2 つの値が等しいかどうかを比較する正しい方法は、次のようになります。

1 == 'a'
# False

ここで起こることは

  1. まず (1).__eq__('a') が試され、これは NotImplemented . これは、その操作がサポートされていないことを示すので
  2. 'a'.__eq__(1) が呼び出され、これもまた同じ NotImplemented . だから
  3. オブジェクトは同じでないものとして扱われ False が返されます。

以下は、カスタムクラスを使ったちょっとしたMCVEで、これがどのように起こるかを説明しています。

class A:
    def __eq__(self, other):
        print('A.__eq__')
        return NotImplemented

class B:
    def __eq__(self, other):
        print('B.__eq__')
        return NotImplemented

class C:
    def __eq__(self, other):
        print('C.__eq__')
        return True

a = A()
b = B()
c = C()

print(a == b)
# A.__eq__
# B.__eq__
# False

print(a == c)
# A.__eq__
# C.__eq__
# True

print(c == a)
# C.__eq__
# True


もちろん、それでは説明がつきません。 なぜ が真を返します。これは NotImplemented は実際には真実の値である。

bool(None.__eq__("a"))
# True

と同じです。

bool(NotImplemented)
# True

どのような値が truthy と falsy とみなされるかについての詳細は、docs のセクションを参照してください。 真偽値のテスト と同様に この答え . ここで注目すべきは NotImplemented は真実であるが、もしこのクラスが __bool__ または __len__ を返したメソッドです。 False または 0 をそれぞれ指定します。


と同等の機能が必要な場合は == 演算子を使用します。 operator.eq :

import operator
operator.eq(1, 'a')
# False

ただし、前述したように この特定のシナリオ をチェックしている場合。 None を使用します。 is :

var = 'a'
var is None
# False

var2 = None
var2 is None
# True

これと機能的に同等なのは operator.is_ :

operator.is_(var2, None)
# True

None は特殊なオブジェクトであり、どの時点でもメモリ上に1バージョンしか存在しない。つまり、このオブジェクトは NoneType クラスへの参照を持つことができる(ただし、同じオブジェクトはいくつでも参照できる)。そのため PEP8ガイドライン は、これを明示します。

のようなシングルトーンとの比較 None は、常に is または is not 等号演算子ではありません。

要約すると、以下のような単数形の場合 None での参照チェックが必要です。 is がより適切ですが、どちらも ==is は問題なく動作します。