このページで解説している内容は、以下の YouTube 動画の解説で見ることができます。

【Python入門】算術演算子の特殊メソッド

算術演算子の特殊メソッド

 ここでは、クラスに算術演算子の特殊メソッドを定義して、自作のオブジェクト同士や数値との演算を行えるようにする方法を学習します。Pythonでは演算子に応じて対応する特殊メソッドが定義されており(__add__, __mul__など)、クラスの使いやすさを大幅に高めることが可能です。座標を表すクラスを例にして、加算演算子と乗算演算子を扱ってみましょう。

プログラムのダウンロード

 「ダウンロード」から実行できるサンプルプログラムがダウンロードできます。ファイルは、ESET Endpoint Securityでウイルスチェックをしておりますが、ダウンロードとプログラムの実行は自己責任でお願いいたします。

1.算術演算子と特殊メソッド

1.1.演算子とメソッドの対応表

Pythonには次のような演算子に対応する特殊メソッドが用意されています。

演算子特殊メソッド動作
+__add__加算
-__sub__減算
*__mul__乗算
@__matmul__行列積
/__truediv__除算(浮動小数点)
//__floordiv__除算(切り捨て)
%__mod__剰余
**__pow__べき乗

 それぞれの演算子をクラスで有効化するには、左側の演算子に対応する特殊メソッドを定義し、処理結果のオブジェクトを作成して返すようにします。

1.2.特殊メソッドの基本構造

def __add__(self, other):
    # 演算結果のオブジェクトを返す
    return 新しいオブジェクト

 引数selfは左オペランド、otherは右オペランドです。たとえば A + B の場合に呼ばれるのが A__add__メソッドであり、otherにはBが入ります。

2.加算演算子 (+) を定義してみる

2.1.コード例

 下記は2次元座標を表すCoordinateクラスです。データ属性にxyを持ち、コンストラクタで値を受け取ります。ここで__add__を定義して、2つのCoordinateオブジェクトを加算できるようにしましょう。

class Coordinate:
    def __init__(self, x, y):
        """2次元の座標(x, y)を初期化"""
        self.x = x
        self.y = y
    
    def __str__(self):
        """print()で座標を表示するときに呼ばれる特殊メソッド
        (x, y) の形式で返すことで、わかりやすい表現にする。
        """
        return f"({self.x}, {self.y})"
    
    def __add__(self, other):
        """
        Coordinate同士の加算を定義。
        それぞれのx,yを合算した新しいオブジェクトを返す。
        """
        return Coordinate(self.x + other.x, self.y + other.y)

def demo_add():
    print("◆加算演算子のデモ◆")
    c1 = Coordinate(3, 4)
    c2 = Coordinate(-1, 6)
    result = c1 + c2  # c1.__add__(c2) が呼ばれる
    print(f"{c1} + {c2} = {result}")  # (3, 4) + (-1, 6) = (2, 10)

if __name__ == "__main__":
    demo_add()
    print("加算演算子のサンプルを終了します。")

解説

  • __init__ : 引数の (x, y) をオブジェクトに格納。
  • __str__print()などで「(3, 4)」のように表示。
  • __add__+ 演算子が呼ばれたときに、自身と相手の座標を加算し、新たな Coordinate を返す。

実行結果は以下のとおりです。

◆加算演算子のデモ◆
(3, 4) + (-1, 6) = (2, 10)
加算演算子のサンプルを終了します。

3.乗算演算子 (*) を定義してみる

3.1.整数との乗算

 今度は__mul__を定義して、オブジェクトと整数を掛け合わせた結果を取得します。たとえば (x, y) * n = (x*n, y*n)のように振る舞うものを作れます。

class Coordinate:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f"({self.x}, {self.y})"
    
    def __add__(self, other):
        return Coordinate(self.x + other.x, self.y + other.y)
    
    def __mul__(self, other):
        """
        Coordinate * 整数 で呼ばれる特殊メソッド。
        x, yを両方とも other倍した新しいCoordinateを返す。
        """
        return Coordinate(self.x * other, self.y * other)

def demo_mul():
    print("◆乗算演算子のデモ◆")
    c1 = Coordinate(1, -2)
    result = c1 * 3  # c1.__mul__(3)
    print(f"{c1} * 3 = {result}")  # (1, -2) * 3 = (3, -6)

if __name__ == "__main__":
    demo_mul()
    print("乗算演算子のサンプルを終了します。")

解説

  • __mul__(self.x * other, self.y * other)の新たなCoordinateを返す。
  • 今回は「整数」を右辺にすることしか考慮していませんが、他の型にも対応したければ型判定を入れるか、NotImplementedを返すようにできます。

実行結果

◆乗算演算子のデモ◆
(1, -2) * 3 = (3, -6)
乗算演算子のサンプルを終了します。

まとめ

  • クラスに算術演算子の特殊メソッドを定義すると、オブジェクト同士やオブジェクトと数値との演算が自然に行えます。
  • __add__+ 演算子のオーバーロード
  • __mul__* 演算子のオーバーロード
  • その他にも__sub____truediv____pow__などがあり、同じ要領で定義可能。
  • 演算子による操作は可読性が高くなる反面、過度なオーバーロードはコードの意図が不透明になる場合もあるため、設計方針をよく考慮して導入してください。

 これらの特殊メソッドを上手く使うことで、独自の数値型や行列型などをPython標準のオブジェクト同様に演算可能にし、操作性を向上させられます。ぜひ様々な演算子の特殊メソッドも試してみてください。