読者です 読者をやめる 読者になる 読者になる

Works by ...

プログラミング関連でメモする

【python】ユーザクラス定義時の==比較定義__eq__()と!=比較定義__ne__()

python

友人にちらと聞いた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__() に委譲して結果を反転させます。

参考文献

http://docs.python.jp/2/reference/datamodel.html