質問

私はオーバーライドしたいクラスを持っています __eq__() オペレーター。私がオーバーライドすべきだというのは理にかなっているようです __ne__() オペレーターも同様ですが、実装するのは理にかなっていますか __ne__ に基づく __eq__ そのような?

class A:
    def __eq__(self, other):
        return self.value == other.value

    def __ne__(self, other):
        return not self.__eq__(other)

それとも、Pythonがこれらの演算子を使用する方法で私が欠けているものはありますか?

役に立ちましたか?

解決

はい、それはまったく問題ありません。実際には、 ドキュメント 定義することをお勧めします __ne__ 定義するとき __eq__:

比較演算子間に暗黙の関係はありません。の真実 x==y それを意味しません x!=y偽です。したがって、定義するとき __eq__(), 、定義する必要があります __ne__() オペレーターが予想どおりに動作するように。

多くの場合(このような)、それはの結果を無効にするのと同じくらい簡単です __eq__, 、 しかしいつもではない。

他のヒント

Python、実装する必要があります __ne__() に基づいてオペレーター __eq__?

短い答え:いいえ。使用 == の代わりに __eq__

Python 3で、 != の否定です == デフォルトでは、あなたは書く必要さえありません __ne__, 、そして、ドキュメントはもはや書くことについて意見を述べていません。

一般的に言えば、Python 3のみのコードの場合、Builtinオブジェクトの場合、親の実装を覆い隠す必要がない限り、それを記述しないでください。

つまり、覚えておいてください レイモンド・ヘッティンガーのコメント:

__ne__ メソッドはから自動的に続きます __eq__ 場合にのみ __ne__ スーパークラスではまだ定義されていません。したがって、ビルトインから相続している場合は、両方をオーバーライドするのが最善です。

Python 2で動作するコードが必要な場合は、Python 2の推奨に従ってください。Python3で機能します。

Python 2では、Python自体は別の操作に関して自動的に操作を実装していません。したがって、次のように定義する必要があります。 __ne__ の面では == の代わりに __eq__。例えば

class A(object):
    def __eq__(self, other):
        return self.value == other.value

    def __ne__(self, other):
        return not self == other # NOT `return not self.__eq__(other)`

その証拠を参照してください

  • 実装 __ne__() に基づいてオペレーター __eq__
  • 実装していません __ne__ Python 2ではまったく

以下のデモで誤った動作を提供します。

長い答え

ドキュメンテーション Python 2の場合:

比較演算子間に暗黙の関係はありません。の真実 x==y それを意味しません x!=y 偽です。したがって、定義するとき __eq__(), 、定義する必要があります __ne__() オペレーターが予想どおりに動作するように。

それは、私たちが定義する場合を意味します __ne__ の逆の観点から __eq__, 、一貫した動作を得ることができます。

ドキュメントのこのセクションは更新されました Python 3:

デフォルトでは、 __ne__() 代表者 __eq__() そうでない限り、結果を反転します NotImplemented.

とで 「新しいもの」セクション, 、この動作が変わったことがわかります:

  • != 今、反対を返します ==, 、 そうでもなければ == 戻り値 NotImplemented.

実装のため __ne__, 、使用することを好みます == オペレーター を使用する代わりに __eq__ IFを直接メソッド self.__eq__(other) サブクラスのリターンの NotImplemented チェックされたタイプの場合、Pythonは適切にチェックします other.__eq__(self) ドキュメントから:

NotImplemented 物体

このタイプには単一の値があります。この値の単一のオブジェクトがあります。このオブジェクトは、内蔵名からアクセスされます NotImplemented. 。数値的方法とリッチ比較方法は、提供されたオペランドの操作を実装しない場合、この値を返す場合があります。 (通訳者は、オペレーターに応じて反射操作または他のフォールバックを試します。)その真理値は真です。

豊富な比較演算子が与えられた場合、それらが同じタイプでない場合、Pythonは other サブタイプであり、そのオペレーターが定義されている場合、 other最初の方法(逆の方法 <, <=, >=>)。もしも NotImplemented 返されます、 それから 反対の方法を使用します。 (します いいえ 同じ方法を2回確認してください。) == オペレーターでは、このロジックを実行できます。


期待

意味的には、実装する必要があります __ne__ あなたのクラスのユーザーは、次の機能がaのすべてのインスタンスと同等であることを期待するため、平等のチェックに関して::

def negation_of_equals(inst1, inst2):
    """always should return same as not_equals(inst1, inst2)"""
    return not inst1 == inst2

def not_equals(inst1, inst2):
    """always should return same as negation_of_equals(inst1, inst2)"""
    return inst1 != inst2

つまり、上記の関数の両方が必要です いつも 同じ結果を返します。しかし、これはプログラマーに依存しています。

定義するときの予期しない行動のデモンストレーション __ne__ に基づく __eq__:

最初にセットアップ:

class BaseEquatable(object):
    def __init__(self, x):
        self.x = x
    def __eq__(self, other):
        return isinstance(other, BaseEquatable) and self.x == other.x

class ComparableWrong(BaseEquatable):
    def __ne__(self, other):
        return not self.__eq__(other)

class ComparableRight(BaseEquatable):
    def __ne__(self, other):
        return not self == other

class EqMixin(object):
    def __eq__(self, other):
        """override Base __eq__ & bounce to other for __eq__, e.g. 
        if issubclass(type(self), type(other)): # True in this example
        """
        return NotImplemented

class ChildComparableWrong(EqMixin, ComparableWrong):
    """__ne__ the wrong way (__eq__ directly)"""

class ChildComparableRight(EqMixin, ComparableRight):
    """__ne__ the right way (uses ==)"""

class ChildComparablePy3(EqMixin, BaseEquatable):
    """No __ne__, only right in Python 3."""

非等価インスタンスのインスタンス:

right1, right2 = ComparableRight(1), ChildComparableRight(2)
wrong1, wrong2 = ComparableWrong(1), ChildComparableWrong(2)
right_py3_1, right_py3_2 = BaseEquatable(1), ChildComparablePy3(2)

予想される行動:

(注:以下のそれぞれの2秒ごとのアサーションは同等であり、したがって、その前のものに対して論理的に冗長性がありますが、私はそれらを含めてそれを示しています 一方が他方のサブクラスである場合、注文は関係ありません。)

これらのインスタンスがあります __ne__ で実装 ==:

assert not right1 == right2
assert not right2 == right1
assert right1 != right2
assert right2 != right1

Python 3でテストするこれらのインスタンスも正しく機能します。

assert not right_py3_1 == right_py3_2
assert not right_py3_2 == right_py3_1
assert right_py3_1 != right_py3_2
assert right_py3_2 != right_py3_1

そして、これらが持っていることを思い出してください __ne__ で実装 __eq__ - これは予想される動作ですが、実装は間違っています。

assert not wrong1 == wrong2         # These are contradicted by the
assert not wrong2 == wrong1         # below unexpected behavior!

予期しない動作:

この比較は上記の比較に矛盾することに注意してください(not wrong1 == wrong2).

>>> assert wrong1 != wrong2
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

と、

>>> assert wrong2 != wrong1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError

スキップしないでください __ne__ Python 2で

実装をスキップすべきではないという証拠 __ne__ Python 2では、これらの同等のオブジェクトを参照してください。

>>> right_py3_1, right_py3_1child = BaseEquatable(1), ChildComparablePy3(1)
>>> right_py3_1 != right_py3_1child # as evaluated in Python 2!
True

上記の結果はあるべきです False!

Python 3ソース

デフォルトのcpython実装 __ne__ 入っています typeobject.cobject_richcompare:

    case Py_NE:
        /* By default, __ne__() delegates to __eq__() and inverts the result,
           unless the latter returns NotImplemented. */
        if (self->ob_type->tp_richcompare == NULL) {
            res = Py_NotImplemented;
            Py_INCREF(res);
            break;
        }
        res = (*self->ob_type->tp_richcompare)(self, other, Py_EQ);
        if (res != NULL && res != Py_NotImplemented) {
            int ok = PyObject_IsTrue(res);
            Py_DECREF(res);
            if (ok < 0)
                res = NULL;
            else {
                if (ok)
                    res = Py_False;
                else
                    res = Py_True;
                Py_INCREF(res);
            }
        }

ここにわかります

しかし、デフォルト __ne__ 使用します __eq__?

Python 3のデフォルト __ne__ Cレベルでの実装の詳細は使用します __eq__ より高いレベルだからです == (pyobject_richcompare)効率が低くなります - したがって、それも処理する必要があります NotImplemented.

もしも __eq__ 正しく実装され、次にの否定 == また、正しいです - そして、それは私たちが私たちの低レベルの実装の詳細を避けることを可能にします __ne__.

使用 == 低レベルのロジックを維持することができます 1 場所、そして 避ける アドレッシング NotImplemented__ne__.

誤ってそれを想定するかもしれません == 戻るかもしれません NotImplemented.

実際には、デフォルトの実装と同じロジックを使用します __eq__, 、アイデンティティをチェックします(参照してください do_richcompare そして以下の私たちの証拠)

class Foo:
    def __ne__(self, other):
        return NotImplemented
    __eq__ = __ne__

f = Foo()
f2 = Foo()

と比較:

>>> f == f
True
>>> f != f
False
>>> f2 == f
False
>>> f2 != f
True

パフォーマンス

私の言葉を見てはいけません。もっとパフォーマンスがあるものを見てみましょう。

class CLevel:
    "Use default logic programmed in C"

class HighLevelPython:
    def __ne__(self, other):
        return not self == other

class LowLevelPython:
    def __ne__(self, other):
        equal = self.__eq__(other)
        if equal is NotImplemented:
            return NotImplemented
        return not equal

def c_level():
    cl = CLevel()
    return lambda: cl != cl

def high_level_python():
    hlp = HighLevelPython()
    return lambda: hlp != hlp

def low_level_python():
    llp = LowLevelPython()
    return lambda: llp != llp

これらのパフォーマンス数はそれ自体を物語っていると思います:

>>> import timeit
>>> min(timeit.repeat(c_level()))
0.09377292497083545
>>> min(timeit.repeat(high_level_python()))
0.2654011140111834
>>> min(timeit.repeat(low_level_python()))
0.3378178110579029

これを考えると、これは理にかなっています low_level_python そうでなければCレベルで処理されるPythonでロジックを実行しています。

一部の批評家への対応

別の応答者は書いています:

アーロンホールの実装 not self == other__ne__ メソッドは、戻ることができないため、間違っています NotImplemented (not NotImplementedFalse)そしてしたがって __ne__ 優先度のある方法は決して後退することはありません __ne__ 優先されない方法。

持っている __ne__ 戻らないでください NotImplemented 間違っていません。代わりに、優先順位付けを処理します NotImplemented との平等のチェックを介して ==. 。仮定します == 正しく実装されており、完了です。

not self == other 以前はデフォルトのPython 3実装でした __ne__ 方法でしたが、それはバグであり、Shadowrangerが気づいたように、2015年1月にPython 3.4で修正されました(問題#21408を参照)。

さて、これを説明しましょう。

前述のように、Python 3はデフォルトで処理します __ne__ 最初に確認することにより self.__eq__(other) 戻り値 NotImplemented (シングルトン) - でチェックする必要があります is もしそうなら返されますが、そうでなければ逆を返すはずです。クラスミックスとして書かれたロジックは次のとおりです。

class CStyle__ne__:
    """Mixin that provides __ne__ functionality equivalent to 
    the builtin functionality
    """
    def __ne__(self, other):
        equal = self.__eq__(other)
        if equal is NotImplemented:
            return NotImplemented
        return not equal

これは、CレベルPython APIの正確性に必要であり、Python 3で導入され、作成が行われました。

冗長。すべて関連性があります __ne__ 独自のチェックを実装するものや、に委任するものを含む方法を削除しました __eq__ 直接または経由 == - と == そうする最も一般的な方法でした。

結論

Python 2互換コードの場合、使用します == 実装する __ne__. 。それはもっとです:

  • 正しい
  • 単純
  • パフォーマンス

Python 3のみで、Cレベルで低レベルの否定を使用します - それは偶数です もっと シンプルでパフォーマンス(プログラマーはそれがあると判断する責任がありますが 正しい).

繰り返しますが、そうしてください いいえ 高レベルのPythonで低レベルのロジックを作成します。

記録のためだけに、標準的に正しく、py2/py3ポータブルを横切る __ne__ 見た目:

import sys

class ...:
    ...
    def __eq__(self, other):
        ...

    if sys.version_info[0] == 2:
        def __ne__(self, other):
            equal = self.__eq__(other)
            return equal if equal is NotImplemented else not equal

これは任意のもので動作します __eq__ あなたは定義するかもしれません:

  • ようではない not (self == other), 、関係するクラスの1つがの結果が結果であることを暗示していない場合、比較を含むいくつかの迷惑/複雑なケースで干渉しないでください __ne__ の結果と同じです not の上 __eq__ (例えば、SqlalchemyのORM、両方 __eq____ne__ 特別なプロキシオブジェクトを返します True また False, 、そしてしようとしています not 結果として __eq__ 戻ってきます False, 、正しいプロキシオブジェクトではなく)。
  • ようではない not self.__eq__(other), 、これは正しく委任します __ne__ 他のインスタンスの場合 self.__eq__ 戻り値 NotImplemented (not self.__eq__(other) なぜなら、余分な間違っているだろうから NotImplemented 真実なので、いつ __eq__ 比較の実行方法がわからなかった、 __ne__ 戻ってきます False, 、実際に尋ねられた唯一のオブジェクトが唯一のオブジェクトが等しくなかった場合、2つのオブジェクトが等しいことを暗示しています。

もしあなたの __eq__ 使用しません NotImplemented 返品、これは(意味のないオーバーヘッドで)使用する場合、機能します NotImplemented 時々、これにより適切に処理されます。そして、Pythonバージョンのチェックは、クラスが import-ed python 3で __ne__ 未定義のままで、Pythonのネイティブで効率的なフォールバックを許可します __ne__ 実装(上記のcバージョン) 引き継ぐ。


なぜこれが必要なのか

Pythonオーバーロードルール

他のソリューションの代わりにこれを行う理由の説明は、やや不可解です。 Pythonには、オペレーターの過負荷に関するいくつかの一般的なルールがあり、特に比較演算子があります。

  1. (すべてのオペレーターに適用)実行時 LHS OP RHS, 、 試す LHS.__op__(RHS), 、そしてそれが戻ってきた場合 NotImplemented, 、 試す RHS.__rop__(LHS). 。例外:if RHS のサブクラスです LHSクラス、テスト RHS.__rop__(LHS) 最初. 。比較演算子の場合、 __eq____ne__ 彼ら自身の「rop」です(したがって、のテスト順序 __ne__LHS.__ne__(RHS), 、 それから RHS.__ne__(LHS), 、逆の場合 RHS のサブクラスです LHS'のクラス)
  2. 「交換」オペレーターのアイデアは別として、演算子間に暗黙の関係はありません。たとえば、同じクラスの場合でも、 LHS.__eq__(RHS) 戻る True 意味しません LHS.__ne__(RHS) 戻り値 False (実際、オペレーターはブール値を返す必要さえありません。SQLalchemyのようなORMは、意図的にそうではなく、より表現力のあるクエリ構文を可能にします)。 Python 3の時点で、デフォルト __ne__ 実装はこのように動作しますが、契約上ではありません。オーバーライドできます __ne__ 厳格な反対ではない方法で __eq__.

これがコンパレータの過負荷に適用する方法

したがって、オペレーターを過負荷にすると、2つのジョブがあります。

  1. 自分で操作を実装する方法を知っている場合は、使用してください それだけ 比較の方法についてのあなた自身の知識(操作の反対側に暗黙的または明示的に委任しないでください。そうすることで、あなたがそれを行う方法に応じて、間違った再帰をリスクリスクします)
  2. もし、あんたが しないでください 自分で操作を実装する方法を知っている、 いつも 戻る NotImplemented, 、そのため、Pythonは他のオペランドの実装に委任できます

の問題 not self.__eq__(other)

def __ne__(self, other):
    return not self.__eq__(other)

反対側に委任しないでください(そして、間違っている場合 __eq__ 適切に戻ります NotImplemented)。いつ self.__eq__(other) 戻り値 NotImplemented (これは「真実」です)、あなたは静かに戻ります False, 、 それで A() != something_A_knows_nothing_about 戻り値 False, 、いつかどうかを確認する必要があります something_A_knows_nothing_about インスタンスと比較する方法を知っていました A, 、そしてそうでない場合、それは返されるべきでした True (どちらの側が他者と比較する方法を知らない場合、それらは互いに等しくないと見なされます)。もしも A.__eq__ 誤って実装されています(返信 False それ以外の NotImplemented 反対側を認識していない場合)、これはから「正しい」です Aの視点、戻ってきます True (以来 A それが等しいとは思わないので、それは平等ではありません)、しかしそれは間違っているかもしれません something_A_knows_nothing_aboutそれは決して尋ねなかったので、の視点 something_A_knows_nothing_about; A() != something_A_knows_nothing_about 終わる True, 、 しかし something_A_knows_nothing_about != A() たぶん......だろう False, 、またはその他の返品値。

の問題 not self == other

def __ne__(self, other):
    return not self == other

もっと微妙です。クラスの99%が正しいでしょう。 __ne__ の論理的な逆です __eq__. 。だが not self == other 上記のルールの両方を破る、つまりクラスを意味する __ne__ そうではありません の論理的逆 __eq__, 、結果は再び反射的ではありません。なぜなら、オペランドの1つが実装できるかどうかを尋ねられないため __ne__ 他のオペランドができない場合でも、まったく。最も単純な例は、戻る変人クラスです False にとって すべて 比較、だから A() == Incomparable()A() != Incomparable() どちらも戻ります False. 。の正しい実装で A.__ne__ (戻ってくるもの NotImplemented 比較の方法がわからない場合)、関係は反射的です。 A() != Incomparable()Incomparable() != A() 結果に同意する(前者の場合、 A.__ne__ 戻り値 NotImplemented, 、 それから Incomparable.__ne__ 戻り値 False, 、後者にいる間、 Incomparable.__ne__ 戻り値 False 直接)。でもいつ A.__ne__ として実装されています return not self == other, A() != Incomparable() 戻り値 True (なぜなら A.__eq__ リターンではありません NotImplemented, 、 それから Incomparable.__eq__ 戻り値 False, 、 と A.__ne__ それを反転させます True)、 その間 Incomparable() != A() 戻り値 False.

このような例を見ることができます ここ.

明らかに、常に戻るクラス False 両方のための __eq____ne__ 少し奇妙です。しかし、前に述べたように、 __eq____ne__ 戻る必要さえありません True/False; SQLalchemy ORMには、クエリビルディングの特別なプロキシオブジェクトを返すコンパレータを含むクラスがあります。 True/False まったく(ブールの文脈で評価された場合、それらは「真実」ですが、そのような文脈で評価されることは決してありません)。

過負荷に失敗することによって __ne__ 適切に、あなた 意思 コードとして、その種のクラスを破る:

 results = session.query(MyTable).filter(MyTable.fieldname != MyClassWithBadNE())

動作します(sqlalchemyが挿入方法を知っていると仮定します MyClassWithBadNE SQL文字列にまったく入ります。これは、タイプアダプターなしで実行できます MyClassWithBadNE まったく協力しなければならない)、予想されるプロキシオブジェクトをに渡す filter, 、 その間:

 results = session.query(MyTable).filter(MyClassWithBadNE() != MyTable.fieldname)

最終的に通過します filter 平野 False, 、 なぜなら self == other プロキシオブジェクトを返します not self == other Truthy Proxyオブジェクトを変換するだけです False. 。うまくいけば、 filter のような無効な引数が処理されることの例外をスローします False. 。私は多くの人がそれを主張するだろうと確信しています MyTable.fieldname したほうがいい 比較の左側に一貫して、一般的なケースでこれを実施するプログラム的な理由はないという事実と正しいジェネリックが残っています __ne__ どちらの方法でも機能します return not self == other 1つの配置でのみ機能します。

簡単な答え:はい(しかし、それを正しく行うためにドキュメントを読んでください)

Shadowrangerの実装 __ne__ 方法は正しいものです(デフォルトのPython 3実装とまったく同じように動作するという意味で):

def __ne__(self, other):
    result = self.__eq__(other)

    if result is not NotImplemented:
        return not result

    return NotImplemented

アーロンホールの実装 not self == other__ne__ メソッドは、戻ることができないため、間違っています NotImplemented (not NotImplementedFalse)そしてしたがって __ne__ 優先度のある方法は決して後退することはありません __ne__ 優先されない方法。 not self == other 以前はデフォルトのPython 3実装でした __ne__ 方法はバグでしたが、Shadowrangerが気づいたように、2015年1月にPython 3.4で修正されました(参照 問題#21408).

比較演算子の実装

Python言語リファレンス Python 3状態の場合 第III章データモデル:

object.__lt__(self, other)
object.__le__(self, other)
object.__eq__(self, other)
object.__ne__(self, other)
object.__gt__(self, other)
object.__ge__(self, other)

これらは、いわゆる「豊富な比較」方法です。演算子シンボルとメソッド名の対応は次のとおりです。 x<y 電話 x.__lt__(y), x<=y 電話 x.__le__(y), x==y 電話 x.__eq__(y), x!=y 電話 x.__ne__(y), x>y 電話 x.__gt__(y), 、 と x>=y電話 x.__ge__(y).

豊富な比較方法がシングルトンを返す場合があります NotImplemented 特定の一組の引数の操作を実装していない場合。

これらのメソッドには、交換されたargumentバージョンはありません(左の引数が操作をサポートしていないが、正しい引数が行う場合に使用される)。それよりも、 __lt__()__gt__() お互いの反省です、 __le__()__ge__() お互いの反省です __eq__()__ne__() 彼ら自身の反省です。オペランドに異なるタイプがあり、右オペランドのタイプが左オペランドのタイプの直接的または間接的なサブクラスである場合、右オペランドの反射方法が優先されます。仮想サブクラス化は考慮されません。

これをPythonコードに変換すると(使用) operator_eq にとって ==, operator_ne にとって !=, operator_lt にとって <, operator_gt にとって >, operator_le にとって <=operator_ge にとって >=):

def operator_eq(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__eq__(left)

        if result is NotImplemented:
            result = left.__eq__(right)
    else:
        result = left.__eq__(right)

        if result is NotImplemented:
            result = right.__eq__(left)

    if result is NotImplemented:
        result = left is right

    return result


def operator_ne(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__ne__(left)

        if result is NotImplemented:
            result = left.__ne__(right)
    else:
        result = left.__ne__(right)

        if result is NotImplemented:
            result = right.__ne__(left)

    if result is NotImplemented:
        result = left is not right

    return result


def operator_lt(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__gt__(left)

        if result is NotImplemented:
            result = left.__lt__(right)
    else:
        result = left.__lt__(right)

        if result is NotImplemented:
            result = right.__gt__(left)

    if result is NotImplemented:
        raise TypeError(f"'<' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result


def operator_gt(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__lt__(left)

        if result is NotImplemented:
            result = left.__gt__(right)
    else:
        result = left.__gt__(right)

        if result is NotImplemented:
            result = right.__lt__(left)

    if result is NotImplemented:
        raise TypeError(f"'>' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result


def operator_le(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__ge__(left)

        if result is NotImplemented:
            result = left.__le__(right)
    else:
        result = left.__le__(right)

        if result is NotImplemented:
            result = right.__ge__(left)

    if result is NotImplemented:
        raise TypeError(f"'<=' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result


def operator_ge(left, right):
    if type(left) != type(right) and isinstance(right, type(left)):
        result = right.__le__(left)

        if result is NotImplemented:
            result = left.__ge__(right)
    else:
        result = left.__ge__(right)

        if result is NotImplemented:
            result = right.__le__(left)

    if result is NotImplemented:
        raise TypeError(f"'>=' not supported between instances of '{type(left).__name__}' and '{type(right).__name__}'")

    return result

比較方法のデフォルトの実装

ドキュメントが追加します:

デフォルトでは、 __ne__() 代表者 __eq__() そうでない限り、結果を反転します NotImplemented. 。比較演算子の間に他の暗黙の関係はありません。たとえば、の真実は (x<y or x==y) 意味しません x<=y.

比較方法のデフォルトの実装(__eq__, __ne__, __lt__, __gt__, __le____ge__)したがって、次のように与えることができます。

def __eq__(self, other):
    return NotImplemented

def __ne__(self, other):
    result = self.__eq__(other)

    if result is not NotImplemented:
        return not result

    return NotImplemented

def __lt__(self, other):
    return NotImplemented

def __gt__(self, other):
    return NotImplemented

def __le__(self, other):
    return NotImplemented

def __ge__(self, other):
    return NotImplemented

したがって、これは正しい実装です __ne__ 方法。そして、それは常にの逆を返すとは限りません __eq__ 方法のため __eq__ メソッド返品 NotImplemented, 、その逆 not NotImplementedFalse (なので bool(NotImplemented)True)目的の代わりに NotImplemented.

の誤った実装 __ne__

アーロンホールが上で示したように、 not self.__eq__(other) のデフォルトの実装ではありません __ne__ 方法。 しかし、そうではありません not self == other. 後者は、デフォルトの実装の動作との動作を比較することにより、以下に示します。 not self == other 2つのケースでの実装:

  • __eq__ メソッド返品 NotImplemented;
  • __eq__ メソッドは、とは異なる値を返します NotImplemented.

デフォルトの実装

どのときに何が起こるか見てみましょう A.__ne__ メソッドは、デフォルトの実装とを使用します A.__eq__ メソッド返品 NotImplemented:

class A:
    pass


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) == "B.__ne__"
  1. != 電話 A.__ne__.
  2. A.__ne__ 電話 A.__eq__.
  3. A.__eq__ 戻り値 NotImplemented.
  4. != 電話 B.__ne__.
  5. B.__ne__ 戻り値 "B.__ne__".

これは、それを示しています A.__eq__ メソッド返品 NotImplemented, 、 A.__ne__ メソッドはに戻ります B.__ne__ 方法。

それでは、いつになると何が起こるか見てみましょう A.__ne__ メソッドは、デフォルトの実装とを使用します A.__eq__ メソッドは、とは異なる値を返します NotImplemented:

class A:

    def __eq__(self, other):
        return True


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) is False
  1. != 電話 A.__ne__.
  2. A.__ne__ 電話 A.__eq__.
  3. A.__eq__ 戻り値 True.
  4. != 戻り値 not True, 、 あれは False.

これは、この場合、 A.__ne__ メソッドはの逆を返します A.__eq__ 方法。したがって __ne__ メソッドは、ドキュメントで宣伝されているように動作します。

デフォルトの実装をオーバーライドします A.__ne__ 上記の正しい実装を使用した方法は、同じ結果をもたらします。

not self == other 実装

デフォルトの実装をオーバーライドするときに何が起こるか見てみましょう A.__ne__ の方法 not self == other 実装と A.__eq__ メソッド返品 NotImplemented:

class A:

    def __ne__(self, other):
        return not self == other


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) is True
  1. != 電話 A.__ne__.
  2. A.__ne__ 電話 ==.
  3. == 電話 A.__eq__.
  4. A.__eq__ 戻り値 NotImplemented.
  5. == 電話 B.__eq__.
  6. B.__eq__ 戻り値 NotImplemented.
  7. == 戻り値 A() is B(), 、 あれは False.
  8. A.__ne__ 戻り値 not False, 、 あれは True.

のデフォルトの実装 __ne__ 返された方法 "B.__ne__", 、 いいえ True.

次に、デフォルトの実装をオーバーライドするときに何が起こるか見てみましょう A.__ne__ の方法 not self == other 実装と A.__eq__ メソッドは、とは異なる値を返します NotImplemented:

class A:

    def __eq__(self, other):
        return True

    def __ne__(self, other):
        return not self == other


class B:

    def __ne__(self, other):
        return "B.__ne__"


assert (A() != B()) is False
  1. != 電話 A.__ne__.
  2. A.__ne__ 電話 ==.
  3. == 電話 A.__eq__.
  4. A.__eq__ 戻り値 True.
  5. A.__ne__ 戻り値 not True, 、 あれは False.

のデフォルトの実装 __ne__ メソッドも返されました False この場合。

この実装は、のデフォルトの実装の動作を再現できないため __ne__ メソッドの場合 __eq__ メソッド返品 NotImplemented, 、それは間違っています。

すべての場合 __eq__, __ne__, __lt__, __ge__, __le__, 、 と __gt__ クラスには意味があり、実装するだけです __cmp__ 代わりは。そうでなければ、ダニエル・ディパオロが言ったので、あなたがやっているようにしてください(私はそれを見上げるのではなくテストしていました;))

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top