Python、に__ne__()
基づいて演算子を実装する必要があり__eq__
ますか?
短い答え:あなたがしなければならない場合の髪型、使用して実装ではなく==
、ではありません__eq__
Python 3では、!=
は==
デフォルトでの否定であるため、を作成する必要すらありません__ne__
。また、ドキュメントを作成することについて、ドキュメントはもはや考えられていません。
一般的に言って、Python 3のみのコードの場合、組み込みオブジェクトなどの親の実装に影を付ける必要がない限り、コードを記述しないでください。
つまり、レイモンドヘッティンガーのコメントを覚えておいてください。
この__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__
メソッドを直接使用するのではなく演算子を使用することをお勧めします。これ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)
予想される行動:
(注:以下の各毎秒アサーションが同等であるため、論理的にその前の1への冗長ながら、私は彼らがそれを証明するために含めています、一方が他方のサブクラスであるときの順序は重要ではありません。)
これらのインスタンスは次のように__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.c
ありobject_richcompare
ます。
case Py_NE:
/* By default, __ne__() delegates to __eq__() and inverts the result,
unless the latter returns NotImplemented. */
if (Py_TYPE(self)->tp_richcompare == NULL) {
res = Py_NotImplemented;
Py_INCREF(res);
break;
}
res = (*Py_TYPE(self)->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);
}
}
break;
しかし、デフォルトで__ne__
は__eq__
?
より高いレベル(PyObject_RichCompare)は効率が低いため__ne__
、CレベルでのPython 3のデフォルトの実装詳細が使用します。したがって、これも処理する必要があります。__eq__
==
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
、Pythonでロジックを実行している場合にCレベルで処理されることになると考えると理にかなっています。
一部の批評家への対応
別の回答者はこう書いている:
Aaron Hallのメソッドの実装not self == other
は、__ne__
決して戻ることができないNotImplemented
(not NotImplemented
is False
)ため、正しくありません。したがって、__ne__
優先順位のあるメソッドは、優先順位のない__ne__
メソッドにフォールバックできません。
持っ__ne__
決して復帰することはNotImplemented
、それは間違ったことはありません。代わりに、NotImplemented
との等価性のチェックを介してで優先順位付けを処理し==
ます。仮定すると、==
正しく実装されて、私たちは完了です。
not self == other
以前は__ne__
メソッドのデフォルトのPython 3実装でしたが、それはバグであり、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__
直接または経由==
-そして==
そうすることの最も一般的な方法でした。
対称性は重要ですか?
私たちの執念深い批評家は、何よりも対称性を重視してで処理NotImplemented
すること__ne__
を主張する病理学的例を提供します。明確な例を挙げて議論を鋼鉄でやろう:
class B:
"""
this class has no __eq__ implementation, but asserts
any instance is not equal to any other object
"""
def __ne__(self, other):
return True
class A:
"This class asserts instances are equivalent to all other objects"
def __eq__(self, other):
return True
>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, False, True)
したがって、このロジックでは、対称性を維持するために__ne__
、Pythonのバージョンに関係なく、複雑なを記述する必要があります。
class B:
def __ne__(self, other):
return True
class A:
def __eq__(self, other):
return True
def __ne__(self, other):
result = other.__eq__(self)
if result is NotImplemented:
return NotImplemented
return not result
>>> A() == B(), B() == A(), A() != B(), B() != A()
(True, True, True, True)
どうやら、これらのインスタンスが等しいことも等しくないことも気にする必要はありません。
対称性は賢明なコードの推定よりも重要ではなく、ドキュメントのアドバイスに従うことを提案します。
ただし、Aにの賢明な実装がある場合は、__eq__
ここで私の方向性をたどることができ、対称性が維持されます。
class B:
def __ne__(self, other):
return True
class A:
def __eq__(self, other):
return False # <- this boolean changed...
>>> A() == B(), B() == A(), A() != B(), B() != A()
(False, False, True, True)
結論
Python 2互換コードの場合は、==
を実装するために使用します__ne__
。それ以上です:
Python 3のみで、Cレベルで低レベルの否定を使用します。これはさらにシンプルでパフォーマンスが高くなります(ただし、プログラマが正しいかどうかを判断する必要があります)。
ここでも、高レベルのPythonで低レベルのロジックを記述しないでください。
__ne__
使って__eq__
、あなたがを使って実装することを勧めません。