< 演算子の実処理は以下のようなものです。
def impl_lt(lhs, rhs): # first try with (lhs < rhs) ret = lhs.__lt__(rhs) if ret != NotImplemented: return ret # second try with (rhs > lhs) ret = rhs.__gt__(lhs) if ret != NotImplemented: return ret # give up operator < raise TypeError最初に、最も素直な呼び出しである lhs.__lt__(rhs) を試します。
最初の呼び出しが NotImplemented を返すと、フォールバックとして、形は異なるが同じ意味であるはずの rhs.__gt__(lhs) を試します。
どちらも NotImplemented であれば、例外を投げて終了します。
フォールバックが機能する例を示します。
以下のような、自身と同じ型との __gt__() だけを備えたクラスを考えます。
class Stored(object): def __init__(self, value): self.value = value def __gt__(self, rhs): # > if isinstance(rhs, Stored): print('Stored > Stored') return self.value > rhs.value return NotImplemented # 素直に Stored.__gt__(Stored) が呼ばれる >>> Stored(2) > Stored(1) Stored > Stored True # Stored.__lt__(Stored) が定義されていないので、 # 代わりに Stored.__gt__(Stored) が呼ばれる >>> Stored(1) < Stored(2) Stored > Stored TrueStoredクラスは __gt__() しか持っていなくとも、< 演算子で結果を得ることができました。これがフォールバックが働いた例となります。
フォールバックが特に役立つのは、分数クラスのように int や float と比較可能なクラスを自作した場合です。
int.__lt__(Fraction) は実装できませんが、Fraction側で Fraction.__lt__(int) などを実装しておけば、フォールバックが働くことで、1 < Fraction(1, 2) のような演算子を呼び出し可能となります。
0 件のコメント:
コメントを投稿