Pythonでの演算子のオーバーロード

Pythonでの演算子オーバーロード特殊メソッドで実現できます。

全部網羅してるわけではないですが、いくつか列挙してみます。

比較演算子系のオーバーロード

特殊メソッド名
__lt__(self, other) self < other
__le__(self, other) self <= other
__eq__(self, other) self == ohter
__ne__(self, other) self != other
__gt__(self, other) self > other
__ge__(self, other) self >= other

特殊メソッドが呼ばれているかprintを入れて確認してみます。

class Foo():
    def __init__(self,x):
        self.__x = x
    def __lt__(self, other):
        print("__lt__")
        return self.__x < other
    def __le__(self, other):
        print("__le__")
        return self.__x <= other
    def __eq__(self, other):
        print("__eq__")
        return self.__x == other
    def __ne__(self, other):
        print("__ne__")
        return self.__x != other
    def __gt__(self, other):
        print("__gt__")
        return self.__x > other
    def __ge__(self, other):
        print("__ge__")
        return self.__x >= other

x = Foo(100)
x < 10
x <= 10
x == 10
x != 10
x > 10
x >= 10
$ py main.py
__lt__
__le__
__eq__
__ne__
__gt__
__ge__

算術演算子系のオーバーロード(左辺)

特殊メソッド名
__add__(self, other) self + other
__sub__(self, other) self - other
__mul__(self, other) self * other
__truediv__(self, other) self / other
__floordiv__(self, other) self // other
__mod__(self, other) self % other
__pow__(self, other) self ** other
__lshift__(self, other) self << other
__rshift__(self, other) self >> other
__and__(self, other) self & other
__xor__(self, other) self ^ other
__or__(self, other) self | other

これは基本的には計算結果をreturnするだけなのですが、注意点としては自身と同じクラスの別オブジェクトとして返すところですね。

class Foo():
    def __init__(self,x):
        self.__x = x
    def __add__(self, other):
        print("__add__")
        return self.__class__(self.__x + other)
    def __sub__(self, other):
        print("__sub__")
        return self.__class__(self.__x - other)
    def __mul__(self, other):
        print("__mul__")
        return self.__class__(self.__x * other)
    def __truediv__(self, other):
        print("__truediv__")
        return self.__class__(self.__x / other)
    def __floordiv__(self, other):
        print("__floordiv__")
        return self.__class__(self.__x // other)
    def __mod__(self, other):
        print("__mod__")
        return self.__class__(self.__x % other)
    def __pow__(self, other):
        print("__pow__")
        return self.__class__(self.__x ** other)
    def __lshift__(self, other):
        print("__lshift__")
        return self.__class__(self.__x << other)
    def __rshift__(self, other):
        print("__rshift__")
        return self.__class__(self.__x >> other)
    def __and__(self, other):
        print("__and__")
        return self.__class__(self.__x & other)
    def __xor__(self, other):
        print("__xor__")
        return self.__class__(self.__x ^ other)
    def __or__(self, other):
        print("__or__")
        return self.__class__(self.__x | other)

x = Foo(100)
x + 10
x - 10
x * 10
x / 10
x // 10
x % 10
x ** 10
x << 10
x >> 10
x & 10
x ^ 10
x | 10
$ py main.py
__add__
__sub__
__mul__
__truediv__
__floordiv__
__mod__
__pow__
__lshift__
__rshift__
__and__
__xor__
__or__

ちなみにreturn時にFooを使わずにself.__class__を使っているのは継承を考慮した場合の書き方です。

算術演算子系のオーバーロード(右辺)

特殊メソッド名
__radd__(self, other) other + self
__rsub__(self, other) other - self
__rmul__(self, other) other * self
__rtruediv__(self, other) other / self
__rfloordiv__(self, other) other // self
__rmod__(self, other) other % self
__rpow__(self, other) other ** self
__rlshift__(self, other) other << self
__rrshift__(self, other) other >> self
__rand__(self, other) other & self
__rxor__(self, other) other ^ self
__ror__(self, other) other | self

先ほどのは左辺のクラスの場合の特殊メソッドでしたが、こちらは対象が右辺のクラスの場合に動作します。

つまり「x + 10」ならx.__add__(10)が起動し、「10 + x」ならx.__radd__(10)が起動するといったかんじです。

実際に__radd__の動作確認をしてみます。

class Foo():
    def __init__(self,x):
        self.__x = x
    def __add__(self, other):
        print("__add__")
        return self.__class__(self.__x + other)
    def __radd__(self, other):
        print("__radd__")
        return self.__class__(self.__x + other)

x = Foo(100)
x + 10
10 + x
$ py main.py
__add__
__radd__


さてここで疑問が生じます、左辺のクラスに__add__が存在し、右辺のクラスに__radd__が存在する場合はどちらが呼ばれるんでしょう。

class Foo():
    def __add__(self, other):
        print("__add__")
        
class Bar():
    def __radd__(self, other):
        print("__radd__")

x = Foo()
y = Bar()
x + y
$ py main.py
__add__

両方存在している場合は__add__が優先されるかんじですね。

算術代入演算子系のオーバーロード

__iadd__(self, other) self += other
__isub__(self, other) self -= other
__imul__(self, other) self *= other
__itruediv__(self, other) self /= other
__ifloordiv__(self, other) self //= other
__imod__(self, other) self %= other
__ipow__(self, other) self **= other
__ilshift__(self, other) self <<= other
__irshift__(self, other) self >>= other
__iand__(self, other) self &= other
__ixor__(self, other) self ^= other
__ior__(self, other) self |= other

これは計算した後、自身への代入を行う演算子オーバーロードですね。

内部的にはselfの値を変更しselfをreturnすると辻褄が合います。

class Foo():
    def __init__(self,x):
        self.__x = x
    def __iadd__(self, other):
        print("__iadd__")
        self.__x += other
        return self
    def __isub__(self, other):
        print("__isub__")
        self.__x -= other
        return self
    def __imul__(self, other):
        print("__imul__")
        self.__x *= other
        return self
    def __itruediv__(self, other):
        print("__itruediv__")
        self.__x /= other
        return self
    def __ifloordiv__(self, other):
        print("__ifloordiv__")
        self.__x //= other
        return self
    def __imod__(self, other):
        print("__imod__")
        self.__x %= other
        return self
    def __ipow__(self, other):
        print("__ipow__")
        self.__x **= other
        return self
    def __ilshift__(self, other):
        print("__ilshift__")
        self.__x <<= other
        return self
    def __irshift__(self, other):
        print("__irshift__")
        self.__x >>= other
        return self
    def __iand__(self, other):
        print("__iand__")
        self.__x &= other
        return self
    def __ixor__(self, other):
        print("__ixor__")
        self.__x ^= other
        return self
    def __ior__(self, other):
        print("__ior__")
        self.__x |= other
        return self

x = Foo(100)
x += 1
x = Foo(100)
x -= 1
x = Foo(100)
x *= 1
x = Foo(100)
x /= 1
x = Foo(100)
x //= 1
x = Foo(100)
x %= 1
x = Foo(100)
x **= 1
x = Foo(100)
x <<= 1
x = Foo(100)
x >>= 1
x = Foo(100)
x &= 1
x = Foo(100)
x ^= 1
x = Foo(100)
x |= 1
$ py main.py
__iadd__
__isub__
__imul__
__itruediv__
__ifloordiv__
__imod__
__ipow__
__ilshift__
__irshift__
__iand__
__ixor__
__ior__

単項演算子系のオーバーロード

特殊メソッド名
__neg__(self) -self
__pos__(self) +self
__invert__(self) ~self

単項演算子系です。これも確認しておきます。

class Foo():
    def __init__(self,x):
        self.__x = x
    def __neg__(self):
        print("__neg__")
        return self.__class__(-self.__x)
    def __pos__(self):
        print("__pos__")
        return self.__class__(+self.__x)
    def __invert__(self):
        print("__invert__")
        return self.__class__(~self.__x)

x = Foo(100)
-x
+x
~x
$ py main.py
__neg__
__pos__
__invert__