友人にちらと聞いたpython話のメモです。
pythonのユーザ定義クラスでは、Javaでequals()メソッドをオーバライドするように、同値(または同一)比較の再定義をすることができます。これはpythonの特殊メソッド(今まで組み込み関数と混同してました・・・)と呼ばれる全てのオブジェクト型が持つメソッドのうち、__eq__()メソッドをオーバライドすることで可能になります。
class Hoge(object): def __init__(self, value): self.value = value def __eq__(self, other): return self.value == other.value hoge1 = Hoge(1) hoge2 = Hoge(1) hoge3 = Hoge(2) print hoge1 == hoge2 # True print hoge1 == hoge3 # False
しかし2系のpythonにおいては、__eq__()関数のみの定義だけでは、以下の様な「等しくないかどうか」の判定を正しく行うことができません。
print hoge1 != hoge2 # True (!?) print hoge1 != hoge3 # True
pythonのクラスは、==の定義を記述する__eq__()のほかに__ne__()関数を持ち、正しい比較を行いたい場合はこれら2つの関数をオーバロードする必要があります。これはどちらかといえばC++の==演算子と!=演算子のオーバロードに近いような気がします。
class Hoge(object): def __init__(self, value): self.value = value def __eq__(self, other): return self.value == other.value hoge1 = Hoge(1) hoge2 = Hoge(1) hoge3 = Hoge(2) print hoge1 == hoge2 # True print hoge1 == hoge3 # False print hoge1 != hoge2 # False print hoge1 != hoge3 # True
python2リファレンスにも同様のことが記述されています。
比較演算子間には、暗黙的な論理関係はありません。すなわち、 x==y が真である場合、暗黙のうちに x!=y が偽になるわけではありません。従って、 __eq__() を実装する際、演算子が期待通りに動作するようにするために __ne__() も定義する必要があります。
ちなみに修正前の!=判定でいずれもTrueを返戻していたのは、python2系ではデフォルトの__ne__()がTrueを返すからです。同様に、デフォルトの__eq__()はいかなる場合もFalseを返します。
class Hoge(object): def __init__(self, value): self.value = value hoge1 = Hoge(1) hoge2 = Hoge(1) hoge3 = Hoge(2) print hoge1 == hoge2 # False print hoge1 == hoge3 # False print hoge1 != hoge2 # True print hoge1 != hoge3 # True
なおpython3系ではこの__ne__()の問題が少し解消しているようで、__ne__()関数が__eq__()に処理を委譲し、__eq__()の結果を反転して返す実装になっているとのことで、__eq__()を実装するだけで良さそうです。
以下python3リファレンスより
デフォルトでは __ne__() は NotImplemented でない限り __eq__() に委譲して結果を反転させます。