`if None.__eq__("a")` が True と評価されるように見えるのはなぜですか (ただし、完全にはそうではありません)? 質問する

`if None.__eq__(

Python 3.7 で次のステートメントを実行すると、(私のテストでは) 次のように出力されますb

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

ただし、None.__eq__("a")は と評価されますNotImplemented

当然、"a".__eq__("a")は に評価されTrue"b".__eq__("a")は に評価されますFalse

最初に関数の戻り値をテストしているときにこれを発見しましたが、2 番目のケースでは何も返さなかったため、関数は を返しましたNone

何が起きてる?

ベストアンサー1

__dunder__これは、メソッドを直接使用すべきではない理由を示す良い例です。==メソッドは、同等の演算子の適切な代替ではないことが非常に多いためです。等価比較には代わりに演算子を使用する必要があります。または、この特殊なケースでは、 をチェックするときにNoneを使用しますis(詳細については、回答の最後までスキップしてください)。

完了しました

None.__eq__('a')
# NotImplemented

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

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

もちろん、それだけでは説明できないなぜこの操作は true を返します。これは、NotImplemented実際には真の値であるためです。

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

と同じ、

bool(NotImplemented)
# True

どのような値が真偽値とみなされるかの詳細については、ドキュメントのセクションを参照してください。真理値テスト、 同様にこの答えここで注目すべきは、 が真実であるということですが、クラスがそれぞれまたは を返すまたはメソッドNotImplementedを定義していたら、話は違っていたでしょう。__bool____len__False0


演算子と同等の機能が必要な場合は==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使用して行う必要があり、等価演算子は使用しないでください。isis not

要約すると、 のようなシングルトンの場合、 と の両方でも問題なく動作しますが、Noneを使用した参照チェックの方isが適切です。==is

おすすめ記事