__eq__はPythonでどのように処理されますか?


96

Pythonは比較演算子の左/右バージョンを提供しないので、どの関数を呼び出すかをどのように決定するのですか?

class A(object):
    def __eq__(self, other):
        print "A __eq__ called"
        return self.value == other
class B(object):
    def __eq__(self, other):
        print "B __eq__ called"
        return self.value == other

>>> a = A()
>>> a.value = 3
>>> b = B()
>>> b.value = 4
>>> a == b
"A __eq__ called"
"B __eq__ called"
False

これは両方の__eq__関数を呼び出すようです。

公式の決定木を探しています。

回答:


119

a == b式は呼び出すA.__eq__ことが存在するため、。そのコードが含まれていself.value == otherます。intは自分自身をBと比較する方法がわからないため、PythonはB.__eq__自分自身をintと比較する方法を知っているかどうかを確認するために呼び出しを試みます。

コードを修正して、比較されている値を表示する場合:

class A(object):
    def __eq__(self, other):
        print("A __eq__ called: %r == %r ?" % (self, other))
        return self.value == other
class B(object):
    def __eq__(self, other):
        print("B __eq__ called: %r == %r ?" % (self, other))
        return self.value == other

a = A()
a.value = 3
b = B()
b.value = 4
a == b

それは印刷されます:

A __eq__ called: <__main__.A object at 0x013BA070> == <__main__.B object at 0x013BA090> ?
B __eq__ called: <__main__.B object at 0x013BA090> == 3 ?

68

Python2.xはを見るa == bと、次のことを試みます。

  • 場合はtype(b)、新しいスタイルのクラスである、とtype(b)のサブクラスでtype(a)あり、type(b)オーバーライドしている__eq__、結果はありますb.__eq__(a)
  • type(a)がオーバーライドされた場合__eq__(つまり、type(a).__eq__でない場合object.__eq__)、結果はになりa.__eq__(b)ます。
  • type(b)オーバーライドされた場合__eq__、結果はになりb.__eq__(a)ます。
  • 上記のいずれにも該当しない場合、Pythonはを探してプロセスを繰り返します__cmp__。存在する場合、オブジェクトはそれが返す場合と同じzeroです。
  • 最後のフォールバックとして、Pythonはを呼び出しますobject.__eq__(a, b)。これはTrueiff abあり、同じオブジェクトです。

特別なメソッドのいずれかがを返すNotImplemented場合、Pythonはそのメソッドが存在しないかのように動作します。

慎重なお、最後のステップ:どちらの場合aも、b過負荷==、その後、a == b同じですa is b


https://eev.ee/blog/2012/03/24/python-faq-equality/から


1
ええと、Python 3のドキュメントが間違っていたようです。詳細は、bugs.python.org / issue4395およびパッチを参照してください。TLDR:rhs上であっても、サブクラスが最初に比較されます。
最大

こんにちは、素晴らしいポスト。最初の箇条書きが文書化されている場所と、なぜそれがそのように設計されているのかを説明できますか?
2015

1
うん、これはpython 2用にどこに文書化されていますか?PEPですか?
Mr_and_Mrs_D 2016年

この回答とそれに伴うコメントに基づいて、これは私を以前よりも混乱させただけです。
Sajuuk

ところで、__eq__ ==をオーバーライドするのに十分ではない、あるタイプのインスタンスだけにバインドされたメソッドを定義していますか?
Sajuuk

2

この質問に対するPython 3の更新された回答を書いています。

どのようにされた__eq__Pythonで、どのような順序で処理?

a == b

一般に理解されていa == bますがa.__eq__(b)、常にそうであるとは限りませんtype(a).__eq__(a, b)

明示的に、評価の順序は次のとおりです。

  1. 場合bのタイプの厳密なサブクラス(ない同じタイプ)であるaS型」と持って__eq__、それを呼び出すとの比較が実施された場合に値を返し、
  2. そうでなければ、a持って__eq__いる場合、それを呼び出し、比較が実装されている場合はそれを返します。
  3. それ以外の場合は、bを呼び出さなかったかどうかを__eq__確認し、比較が実装されている場合はそれを呼び出して返します。
  4. それ以外の場合は、最後に、アイデンティティとの比較を行いisます。

メソッドがを返す場合、比較が実装されていないかどうかがわかりますNotImplemented

(Python 2では、__cmp__検索されたメソッドがありましたが、Python 3では廃止され、削除されました。)

BをサブクラスAにして、最初のチェックの動作を自分でテストしてみましょう。これは、受け入れられた回答がこのカウントで間違っていることを示しています。

class A:
    value = 3
    def __eq__(self, other):
        print('A __eq__ called')
        return self.value == other.value

class B(A):
    value = 4
    def __eq__(self, other):
        print('B __eq__ called')
        return self.value == other.value

a, b = A(), B()
a == b

これはB __eq__ called戻る前にのみ印刷されFalseます。

この完全なアルゴリズムをどのように知ることができますか?

ここでの他の答えは、私が情報を更新するつもりですので、不完全で古くなっているようだと、あなた自身のためにこれを調べることができるか、どのように示しています。

これはCレベルで処理されます。

ここでは、2つの異なるコードを確認する必要があり__eq__ます。クラスのオブジェクトのデフォルトと、デフォルトとカスタムのどちらを使用するかに関係なくobject__eq__メソッドを検索して呼び出すコードです__eq__

デフォルト __eq__

探し__eq__にアップし、関連するCのAPIドキュメントのショー私達__eq__によって処理されるtp_richcompareでいる- "object"で型定義cpython/Objects/typeobject.cで定義されているobject_richcompareためcase Py_EQ:

    case Py_EQ:
        /* Return NotImplemented instead of False, so if two
           objects are compared, both get a chance at the
           comparison.  See issue #1393. */
        res = (self == other) ? Py_True : Py_NotImplemented;
        Py_INCREF(res);
        break;

したがって、ここでは、self == otherを返す場合True、それ以外の場合はNotImplementedオブジェクトを返します。これは、独自の__eq__メソッドを実装しないオブジェクトのサブクラスのデフォルトの動作です。

__eq__呼ばれる方法

次に、を呼び出すC APIドキュメントのPyObject_RichCompare関数を見つけますdo_richcompare

次にtp_richcompare"object"C定義用に作成された関数がによって呼び出されていることがわかりますdo_richcompareので、もう少し詳しく見てみましょう。

この関数の最初のチェックは、比較されるオブジェクトの条件です。

  • 同じタイプではありませんが、
  • 2番目の型は最初の型のサブクラスであり、
  • 2番目の型には__eq__メソッドがあり、

次に、引数を入れ替えて相手のメソッドを呼び出し、実装されている場合は値を返します。そのメソッドが実装されていない場合は、続行します...

    if (!Py_IS_TYPE(v, Py_TYPE(w)) &&
        PyType_IsSubtype(Py_TYPE(w), Py_TYPE(v)) &&
        (f = Py_TYPE(w)->tp_richcompare) != NULL) {
        checked_reverse_op = 1;
        res = (*f)(w, v, _Py_SwappedOp[op]);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);

次に__eq__、最初のタイプからメソッドを検索して呼び出すことができるかどうかを確認します。結果がNotImplementedでない、つまり実装されている限り、それを返します。

    if ((f = Py_TYPE(v)->tp_richcompare) != NULL) {
        res = (*f)(v, w, op);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);

それ以外の場合は、他の型のメソッドを試さずにそこにある場合は、それを試し、比較が実装されている場合はそれを返します。

    if (!checked_reverse_op && (f = Py_TYPE(w)->tp_richcompare) != NULL) {
        res = (*f)(w, v, _Py_SwappedOp[op]);
        if (res != Py_NotImplemented)
            return res;
        Py_DECREF(res);
    }

最後に、どちらのタイプにも実装されていない場合に備えて、フォールバックを取得します。

フォールバックは、オブジェクトのIDを確認します。つまり、メモリ内の同じ場所にある同じオブジェクトであるかどうかを確認します。これは、次の場合と同じですself is other

    /* If neither object implements it, provide a sensible default
       for == and !=, but raise an exception for ordering. */
    switch (op) {
    case Py_EQ:
        res = (v == w) ? Py_True : Py_False;
        break;

結論

比較では、最初に比較のサブクラス実装を尊重します。

次に、最初のオブジェクトの実装との比較を試み、次に呼び出されなかった場合は2番目のオブジェクトとの比較を試みます。

最後に、同一性の比較のための同一性のテストを使用します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.