Pythonの__add__メソッドを徹底解説! オブジェクト同士の「足し算」を自由に定義しよう


Pythonで数値や文字列を+演算子で足し算するのは日常的ですよね。でも、自分で作ったクラスのオブジェクト同士を足し算したいと思いませんか? 例えば、2つのベクトルオブジェクトを足し合わせて新しいベクトルを作ったり、カスタムデータ構造を結合したりするような場合です。

そんな時にPythonでその「足し算」の挙動をカスタマイズできるのが、特殊メソッド(マジックメソッド)の1つである**__add__メソッド**です。

この記事では、__add__メソッドの基本的な使い方から、その役割、そしてカスタムオブジェクトに独自の足し算を実装する方法まで、初心者の方にも分かりやすく徹底的に解説します。__add__をマスターすれば、あなたのPythonコードはもっと直感的で、オブジェクト指向プログラミングの幅が格段に広がるでしょう!

__add__メソッドとは? なぜ使うのか?

__add__は、Pythonの特殊メソッド(Special Method)、あるいは**マジックメソッド(Magic Method)**と呼ばれるメソッドの一つです。これらのメソッドは、__(アンダースコア2つ)で始まり、__で終わる名前を持ち、特定のPythonの構文や組み込み関数が呼び出されたときに自動的に実行されます。

__add__メソッドは、オブジェクトに対して**+演算子**が使用されたときに呼び出されるメソッドです。

なぜ__add__を使うのでしょうか?

  • 演算子のオーバーロード(Operator Overloading): 独自のクラスで+演算子が使われたときの動作を、自由に定義できるようになります。これにより、カスタムオブジェクトを数値や文字列のように自然に操作できます。

  • コードの可読性向上: 複雑な処理を関数呼び出しではなく、直感的な+演算子で表現できるため、コードが読みやすくなります。

  • ドメイン固有の操作: 扱うデータや概念に合わせて、意味のある「足し算」を実装できます。例えば、ベクトルの加算、複素数の加算、通貨の合計などです。

__add__メソッドの基本的な使い方

__add__メソッドは、クラスの内部で定義します。通常、2つの引数を受け取ります。

構文

Python
 
class MyClass:
    def __add__(self, other):
        # self: +演算子の左側のオブジェクト (MyClassのインスタンス)
        # other: +演算子の右側のオブジェクト (任意の型)
        # ここで足し算のロジックを実装し、結果を返す
        pass

戻り値

__add__メソッドは、足し算の結果となる新しいオブジェクトを返す必要があります。元のオブジェクト(self)を変更するべきではありません。これは、数値の足し算が元の数値を変更せず、新しい数値を返すのと同じ原則です。

具体例1:二次元ベクトルの加算

最も分かりやすい例として、二次元ベクトルを表現するクラスで__add__を実装してみましょう。

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

    def __repr__(self): # オブジェクト表示用(開発者向け)
        return f"Vector({self.x}, {self.y})"

    def __add__(self, other):
        # other が Vector型かチェック
        if isinstance(other, Vector):
            # 新しい Vector オブジェクトを返す
            return Vector(self.x + other.x, self.y + other.y)
        else:
            # Vector型でない場合はエラーを返すか、適切な処理を行う
            return NotImplemented # 他の型との足し算をサポートしないことを示す

v1 = Vector(1, 2)
v2 = Vector(3, 4)

v3 = v1 + v2 # __add__ メソッドが呼び出される
print(v3)    # 出力: Vector(4, 6)

# 数値との加算はエラーになる (NotImplementedが返されるため)
# print(v1 + 5) # TypeError: unsupported operand type(s) for +: 'Vector' and 'int'

この例では、Vectorオブジェクト同士を+で足し算すると、それぞれのxy成分が加算された新しいVectorオブジェクトが生成されます。

__add__の動作と注意点

1. 戻り値は新しいオブジェクト

先述の通り、__add__新しいオブジェクトを返すべきです。元のオブジェクトをインプレースで変更したい場合は、__iadd__+=演算子用)を使用します。

Python
 
# NG例(__add__でselfを変更すべきではない)
class BadVector:
    def __init__(self, x, y): self.x, self.y = x, y
    def __repr__(self): return f"BadVector({self.x}, {self.y})"
    def __add__(self, other):
        self.x += other.x # BAD! 元のオブジェクトを変更している
        self.y += other.y # BAD!
        return self

bv1 = BadVector(1, 2)
bv2 = BadVector(3, 4)
bv3 = bv1 + bv2
print(bv3) # BadVector(4, 6)
print(bv1) # BadVector(4, 6) # bv1も変更されてしまう!

このように、__add__selfを変更してしまうと、予期せぬ副作用(元のオブジェクトまで変わってしまう)が生じるため、必ず新しいオブジェクトを返してください

2. other引数の型チェック

__add__メソッドは、otherとして任意の型のオブジェクトを受け取る可能性があります。したがって、otherの型をチェックし、適切に処理を分岐させることが重要です。

  • isinstance(other, MyClass): otherが特定のクラスのインスタンスであるかを確認します。

  • NotImplementedを返す: もしotherselfのクラスとの足し算をサポートしない型である場合、NotImplementedを返すべきです。これにより、Pythonはother側の__radd__メソッド(後述)を試したり、最終的にTypeErrorを発生させたりします。TypeErrorを直接発生させるよりも推奨されます。

3. __radd__との関係(Reverse Add)

+演算子で左右のオブジェクトの型が異なる場合、Pythonは特殊なルールでどちらの__add__メソッドを呼び出すかを決定します。

例えば obj1 + obj2 という式があったとき:

  1. obj1.__add__(obj2) が試されます。

  2. もし obj1.__add__(obj2)NotImplementedを返した場合、Pythonは obj2.__radd__(obj1) (リバース加算)を試します。

  3. どちらもNotImplementedを返すか、対応するメソッドが存在しない場合、TypeErrorが発生します。

これにより、例えばMyClassのオブジェクトと組み込み型(例: int)を足し合わせる場合でも、適切に処理を定義できます。

Python
 
class MyNumber:
    def __init__(self, value):
        self.value = value
    def __repr__(self): return f"MyNumber({self.value})"

    def __add__(self, other):
        if isinstance(other, MyNumber):
            return MyNumber(self.value + other.value)
        elif isinstance(other, int):
            return MyNumber(self.value + other)
        return NotImplemented # これが重要

    def __radd__(self, other): # other + self が呼び出された時
        if isinstance(other, int):
            return MyNumber(other + self.value)
        return NotImplemented

num = MyNumber(10)
print(num + MyNumber(5)) # MyNumber(15) (MyNumber.__add__ が呼び出される)
print(num + 3)          # MyNumber(13) (MyNumber.__add__ が呼び出される)
print(5 + num)          # MyNumber(15) (int.__add__ が NotImplemented を返し、MyNumber.__radd__ が呼び出される)

__radd__は、左側のオブジェクトが右側のオブジェクトを処理できない場合に、右側のオブジェクトのメソッドが呼び出されるという「フォールバック」の役割を果たします。

まとめ

__add__メソッドは、Pythonのオブジェクト指向プログラミングにおいて、カスタムクラスに直感的で自然な「足し算」の挙動を与えるための強力なツールです。

  • +演算子の動作をカスタマイズするための特殊メソッド

  • 常に新しいオブジェクトを返すべきで、元のオブジェクトを直接変更しない。

  • other引数の型を適切にチェックし、サポートしない型の場合は**NotImplementedを返す**。

  • __radd__メソッドと連携して、異なる型のオブジェクト間の加算も処理できる。

__add__を使いこなすことで、あなたのカスタムクラスはよりPythonicで、まるで組み込み型のように自然に振る舞うようになります。ぜひ今日学んだことを、あなたのコーディングに活かしてみてくださいね!

■「らくらくPython塾」が切り開く「呪文コーディング」とは?

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座