RubyとPythonにおけるクラスの演算子
RubyやPythonでの 自作クラスと組み込みクラスの間の演算の定義に困ったのでメモ
RubyでもPythonでも 例えば a + b
みたいな二項演算の式は a.+(b)
とか a.__add__(b)
みたいな感じで処理されるっぽい.
このとき,a
が 自作クラスのときは自分で定義した演算子が呼ばれるので困らないけど,
a
が組込クラスでb
が自作クラスのときはa
の演算子が呼ばれるのでb
との演算が定義されてなくて死ぬ.
Ruby
RubyのNumeric
クラスの算術演算子で相手が知らないクラスの場合は,#coerce
ってメソッドを呼んでくれるらしい.
- instance method Numeric#coerce (Ruby 2.4.0)
- class - Does Ruby have right operators similar to Python? - Stack Overflow
これを使えば下のコードのようにうまく定義できた.
class MyClass def initialize(val) @val = val end def +(other) if other.kind_of?(MyClass) return @val + other.val elsif other.kind_of?(Numeric) return @val + other else return TypeError end end def coerce(other) return [self, other] end attr_accessor :val end a = MyClass.new(1) b = MyClass.new(2) puts("1 + 1 = #{1 + 1}") # => 1 + 1 = 2 puts("a + b = #{a + b}") # => a + b = 3 puts("a + 1 = #{a + 1}") # => a + 1 = 2 puts("1 + a = #{1 + a}") # => 1 + a = 2
Python
Pythonは a.__add__(b)
を呼んでみて,NotImplementedが帰ってきたら b.__radd__(a)
を呼んでくれるらしい.
ということで,下のように実装できた.
class MyClass: def __init__(self, val): self.val = val def __add__(self, other): if isinstance(other, (int, float)): return self.val + other elif isinstance(other, MyClass): return self.val + other.val else: return NotImplemented def __radd__(self, other): return self.__add__(other) a = MyClass(1) b = MyClass(2) print("1 + 1 =", 1 + 1) print("a + b =", a + b) print("a + 1 =", a + 1) print("1 + a =", 1 + a)