Pythonの逆演算子(__rand__・__radd__)とは?実用的な使い方を解説
Pythonでは、s & z のような二項演算は、まず左オペランドの特殊メソッドが呼び出されます。これに対して「逆演算子(リフレクテッドメソッド)」とは、左右が入れ替わった状態で呼び出されるメソッド群のことです。
s & z → s.__and__(z)
z & s → s.__rand__(z)
int や str といった組み込み型同士の単純な演算では、逆演算子の出番はほとんどありません。しかし、クラスの継承や異なる型同士の連携を扱う場面では、逆演算子が非常に重要な役割を果たします。
逆演算子が呼び出される2つのケース
1. 左オペランドが NotImplemented を返したとき
Pythonインタプリタは、まず左オペランドの通常のメソッド(例:__and__)を試します。この結果が NotImplemented だった場合、代わりに右オペランドの逆演算子(例:__rand__)が呼び出されます。
2. 右オペランドが左オペランドのサブクラスであるとき
継承関係がある場合、右オペランドの型が左オペランドの型のサブクラスであれば、通常のメソッドよりも先にサブクラス側の逆演算子が試行されます。これにより、親クラスと子クラスでそれぞれ異なる実装を持たせることが可能になります。
具体的なコード例
NotImplemented によるフォールバック
class MyNumber:
def __init__(self, value):
self.value = value
def __add__(self, other):
print("__add__ が呼ばれました")
return MyNumber(self.value + other)
def __radd__(self, other):
print("__radd__ が呼ばれました")
return MyNumber(other + self.value)
n = MyNumber(10)
n + 5 # __add__ が呼ばれる
5 + n # int 側は MyNumber を処理できず、__radd__ が呼ばれる
int.__add__ は未知の型である MyNumber を扱えず NotImplemented を返すため、Pythonは自動的に n.__radd__(5) へ処理を委ねます。この仕組みによって、独自クラスと組み込み型との相互運用性が保たれるのです。
継承時の優先順位の制御
class Base:
def __sub__(self, other):
return "Base.__sub__"
def __rsub__(self, other):
return "Base.__rsub__"
class Child(Base):
def __rsub__(self, other):
return "Child.__rsub__"
Base() - Child() # 結果: "Child.__rsub__"
右辺の Child は Base のサブクラスなので、Base.__sub__ よりも先に Child.__rsub__ が試されます。この挙動を理解しておけば、演算結果がどちらの実装に依存するのかを明確に設計できます。
まとめ
- 逆演算子は、左オペランドのメソッドが
NotImplementedを返した際の代替処理として機能する - 右オペランドが左オペランドのサブクラスの場合、逆演算子が優先的に試行される
- 異なる型同士の演算や、継承階層における柔軟な振る舞いの設計に不可欠な仕組み
-
Pythonのimport文とは?モジュールを使うために必須な理由と基本の使い方
Pythonのコード内でパッケージやモジュールを利用するには、まずそのモジュールを利用可能な状態にしておく必要があります。これを行うのがimport文です。 Pythonでは「定義されていないもの」は使えない Pythonでは、まだ定義されていない名前を参照することはできません。ただし、一部の機能は最初から組み込まれており、int や float などの基本型はいつでも自由に使えます。 しかし、実際にやりたい処理の多くは、標準ライブラリや外部パッケージの力を借りる必要があります。たとえば、三角関数の余弦(コサイン)を計算したい場合、math モジュールをインポートせずに math.cos(0)
-
Pythonのassertステートメントとは?基本構文と使い方を解説
Pythonのassertステートメントとは Pythonのassertステートメントは、プログラム内で開発者が定義した条件(制約)が正しく満たされているかを確認するための仕組みです。主にデバッグ目的で使用され、スクリプトの冒頭などに記述して、前提条件が成り立っているかどうかを検証します。 基本構文 assertステートメントは、以下のような構文で記述します。 assert <some_test>, <message> この行は次のように読み解けます。「もし<some_test>がFalseと評価された場合、例外が発生し、<message>が出