Python演算子オーバーロード完全ガイド:__add__、__lt__など特殊メソッドでOOP設計を強化
Python演算子オーバーロード
Pythonの組み込み演算子はオペランド型によって異なる振る舞いをします。+演算子は数値加算、文字列連結、リスト結合など多様です。この柔軟性は演算子オーバーロードによって実現されており、自作クラスに文脈依存の意味を演算子に割り当てられます。
2次元平面上の座標を表すシンプルなPointクラスを考えます。特殊メソッドなしで2点加算を試みるとTypeErrorが発生します:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
p1 = Point(1, 2)
p2 = Point(2, 3)
print(p1+p2)
出力
Traceback (most recent call last): File "<string>", line 9, in <module> print(p1+p2) TypeError: unsupported operand type(s) for +: 'Point' and 'Point'
Pythonには2つのPointインスタンスをどう結合するか組み込み知識がないため例外を投げます。演算子オーバーロードでこの振る舞いを自前定義できます。
Python特殊メソッド
ダブルアンダースコアで囲まれたメソッド—__init__、__str__、__add__など—は特殊メソッドまたは「ダンダー」メソッドと呼ばれます。これらはPythonランタイムによって暗黙的に呼び出され、演算子オーバーロード、コンテナプロトコルなどのフックポイントとなります。
例えば__str__メソッドはオブジェクトが人間可読文字列に変換される方法を制御します。デフォルトではオブジェクト出力はメモリアドレス表示であまり有用ではありません。__str__をオーバーライドするとデバッグとログが格段に明確になります:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return '({0}, {1})'.format(self.x, self.y)
これでPointを出力すると:
p1 = Point(2, 3)
print(p1)
出力
(2, 3)
同じ表現がstr(p1)とformat(p1)でも使用されます。Pythonは内部的にp1.__str__()を呼び出すためです。
+演算子のオーバーロード
Pointオブジェクトの加算をカスタマイズするには__add__を実装:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return '({0}, {1})'.format(self.x, self.y)
def __add__(self, other):
return Point(self.x + other.x, self.y + other.y)
新しい振る舞いをテスト:
p1 = Point(1, 2)
p2 = Point(2, 3)
print(p1 + p2)
出力
(3, 5)
裏ではPythonがp1.__add__(p2)を実行し、座標和を持つ新しいPointを返します。
他の算術演算子と実装すべき特殊メソッドのクイックリファレンス:
| 演算子 | 式 | 特殊メソッド |
|---|---|---|
| 加算 | p1 + p2 | __add__(self, other) |
| 減算 | p1 - p2 | __sub__(self, other) |
| 乗算 | p1 * p2 | __mul__(self, other) |
| 累乗 | p1 ** p2 | __pow__(self, other) |
| 除算 | p1 / p2 | __truediv__(self, other) |
| 切り捨て除算 | p1 // p2 | __floordiv__(self, other) |
| 剰余 | p1 % p2 | __mod__(self, other) |
| 左シフト | p1 << p2 | __lshift__(self, other) |
| 右シフト | p1 >> p2 | __rshift__(self, other) |
| ビットAND | p1 & p2 | __and__(self, other) |
| ビットOR | p1 | p2 | __or__(self, other) |
| ビットXOR | p1 ^ p2 | __xor__(self, other) |
| ビットNOT | ~p1 | __invert__(self) |
比較演算子のオーバーロード
演算子オーバーロードは算術に限られません。比較演算子もカスタマイズ可能。例えば原点からの距離で点を比較するには__lt__をオーバーライド:
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __str__(self):
return '({0}, {1})'.format(self.x, self.y)
def __lt__(self, other):
self_mag = self.x**2 + self.y**2
other_mag = other.x**2 + other.y**2
return self_mag < other_mag
使用例:
p1 = Point(1, 1)
p2 = Point(-2, -3)
p3 = Point(1, -1)
print(p1 < p2) # True
print(p2 < p3) # False
print(p1 < p3) # False
他の比較メソッドも同様のパターン:
| 演算子 | 式 | 特殊メソッド |
|---|---|---|
| 未満 | p1 < p2 | __lt__(self, other) |
| 以下 | p1 <= p2 | __le__(self, other) |
| 等価 | p1 == p2 | __eq__(self, other) |
| 非等価 | p1 != p2 | __ne__(self, other) |
| 超過 | p1 > p2 | __gt__(self, other) |
| 以上 | p1 >= p2 | __ge__(self, other) |
これらメソッドを慎重に定義することで、カスタムオブジェクトを算術・比較コンテキストで自然に参加させ、コードの可読性と保守性を向上できます。
Python
- Python データ型マスター: 実践ガイド
- Python 演算子完全マスター: 包括的ガイド
- Python pass文完全マスター: 実践ガイド
- Python 関数引数完全マスター: 位置引数・キーワード引数・デフォルト引数
- Python辞書完全ガイド:作成、操作、高度なテクニック
- Python継承マスター:概念、構文、実践例
- Pythonイテレータ完全ガイド:仕組みと自作方法
- Pythonクロージャ解説:ネスト関数が変数を捕捉する仕組み
- Python @propertyデコレータマスター:クリーンで後方互換なゲッター/セッター
- Python datetimeマスター:日付・時刻・タイムゾーン実践ガイド
- Pythonのtime.sleep()をマスター:ベストプラクティス、マルチスレッド、実例