2019年8月3日

演算子の話⑤ functools.total_ordering

前回、__lt__() や __eq__() が定義されていても <= や >= はフォールバックしてくれない、という話をしました。
少し気の利かない仕様のような気がします。
これをフォローしてくれるのが functools の total_ordering です。

total_ordering はデコレータで、__lt__()、__le__()、__gt__()、__ge__() の4つの不等式系関数のうち最低1つと __eq__() を持っているクラスを修飾し、全6つの不等号・等号演算子に対応するようになります。

@total_ordering  # デコレータ
class Stored(object):
    def __init__(self, value):
        self.value = value

    def __lt__(self, rhs):
        return self.value < rhs.value

    def __eq__(self, rhs):
        return self.value == rhs.value

# 全6つの不等号・等号に対応
>>> Stored(1) < Stored(2)
True

>>> Stored(1) <= Stored(2)
True

>>> Stored(1) > Stored(2)
False

>>> Stored(1) >= Stored(2)
False

>>> Stored(1) == Stored(2)
False

>>> Stored(1) != Stored(2)
True
__le__() や __ge__() が定義されていなくても、<= や >= が呼ばれています。

と、一見便利そうな total_ordering ですが、実際にはあまり使う機会がありません。
第一の理由は、わざわざ total_ordering で修飾するのが煩わしい、ためです。__lt__() を定義した時点で < とそのフォールバックの > に対応でき、さらに __eq__() を定義することで == とそのフォールバックの != に対応できます。つまり、total_ordering の前提条件の時点で6つの不等号・等号のうち、4つはカバーできることになります。なので、total_ordering を使うよりも、あと1つ __le__() あたりを定義してした方が手っ取り早い、というわけです。
もう一つの理由は、total_ordering を使うと動作が遅くなる、ためです。不等式は数学的クラスでよく使われますが、数学的クラスでは動作速度が重要となります。そのような使用目的では、total_ordering を使うよりも、愚直に全6つの不等式・等式系関数を定義した方が、動作は速くなります。

0 件のコメント:

コメントを投稿