2019年7月25日

演算子の話③ == 演算子 が行うこと

前回、< 演算子の実処理を見てみました。今回は == 演算子の実処理を見てみます。

== 演算子の実処理は以下のようなものです。

def impl_eq(lhs, rhs):
    # first try with (lhs == rhs)
    ret = lhs.__eq__(rhs)
    if ret != NotImplemented:
        return ret

    # second try with (rhs == lhs)
    ret = rhs.__eq__(lhs)
    if ret != NotImplemented:
        return ret

    # finally use 'is'
    raise lhs is rhs
最初に、最も素直な呼び出しである lhs.__eq__(rhs) を試します。
最初の呼び出しが NotImplemented を返すと、フォールバックとして、形は異なるが同じ意味の呼び出しであるはずの rhs.__eq__(lhs) を試します。
どちらも NotImplemented であれば、最終手段として左辺値と右辺値を is 演算子で比較した結果を返します。
== 演算子の実処理では直接例外が投げられない点が、< 演算子の実処理と異なる点です。

フォールバックが機能する例を示します。
以下のような、自身と同じ型 又は int型との __eq__() だけを備えたクラスを考えます。
class Stored(object):
    def __init__(self, value):
        self.value = value

    def __eq__(self, rhs):  # ==
        if isinstance(rhs, Stored):
            print('Stored == Stored')
            return self.value == rhs.value
        if isinstance(rhs, int):
            print('Stored == int')
            return self.value == rhs
        return NotImplemented

# 素直に Stored.__eq__(Stored) が呼ばれる
>>> Stored(1) == Stored(1)
Stored == Stored
True

# 素直に Stored.__eq__(int) が呼ばれる
>>> Stored(1) == 1
Stored == int
True

# int.__eq__(Stored) が定義されていないので、
# 代わりに Stored.__eq__(int) が呼ばれる
>>> 1 == Stored(1)
Stored == int
True
int.__eq__(Stored) は定義されていませんが、フォールバックにより、1 == Stored(1) の結果を得ることができました。



ちなみに、!= 演算子の実処理は以下のようなものです。
def impl_eq(lhs, rhs):
    # first try with (lhs != rhs)
    ret = lhs.__ne__(rhs)
    if ret != NotImplemented:
        return ret

    # second try with (rhs != lhs)
    ret = rhs.__ne__(lhs)
    if ret != NotImplemented:
        return ret

    # finally use == operation
    return not rhs == lhs
!= 演算子が定義されていなかった場合のフォールバックとして、== 演算子を使います。
== 演算子の実処理では != 演算子を使っていないのに、その逆は成り立っているというのはやや意外な気もします。

また、仮に __eq__() だけ定義されたクラスがあったとして、そのクラスは自動生成された __ne__() を持つことになります。自動生成された __ne__() は、__eq__() の戻り値を反転させたものを返します。

0 件のコメント:

コメントを投稿