はじめに
移植やってます。
( from python 3.7 to ruby 2.7 )
多重継承 (Python)
移植するにあたり、多重継承はRuby
のミックスイン
で行こうと思っていました。
__init__
のないクラスをモジュールにする予定でしたが、これでは避けられないようですので、委譲により多重継承を表現できればと思います。
forwardable (Ruby)
require 'forwardable'
class A
def initialize(s)
@s = s
end
def putsa(o)
puts "#{@s}は#{o}です"
end
def putsd()
puts "呼び出されたクラスは#{self.class}です"
end
end
class B
def initialize(s)
@s = s
end
def putsb(o)
puts "#{@s}は#{o}です"
end
def putsd()
puts "呼び出されたクラスは#{self.class}です"
end
end
class C
extend Forwardable
def initialize(*args, **kwargs)
@a = A.new(kwargs['A'])
@b = B.new(kwargs['B'])
end
def_delegators :@a, :putsa, :putsd
def_delegators :@b, :putsb, :putsd
end
c = C.new('A' => '好きな食べ物', 'B' => '好きな飲み物')
c.putsa('みかん')
c.putsb('コーヒー')
c.putsd()
p C.ancestors
# output
好きな食べ物はみかんです
好きな飲み物はコーヒーです
呼び出されたクラスはBです
[C, Object, Kernel, BasicObject]
学習したばかりのforwardable
を使用した例です。
メソッドが重複した場合、後の def_delegators :@b, :putsb, :putsd
が優先されるようです。
また、ancestors
にクラスA
もB
も検索されませんので、super
で呼び出したりとかはできないものと思われます。
delegate (Ruby)
require 'delegate'
class A
def initialize(s)
@s = s
end
def putsa(o)
puts "#{@s}は#{o}です"
end
def putsd()
puts "呼び出されたクラスは#{self.class}です"
end
end
class B
def initialize(s)
@s = s
end
def putsb(o)
puts "#{@s}は#{o}です"
end
def putsd()
puts "呼び出されたクラスは#{self.class}です"
end
end
class C
def initialize(*args, **kwargs)
@a = SimpleDelegator.new(A).new(kwargs['A'])
@b = SimpleDelegator.new(B).new(kwargs['B'])
end
def method_missing(name, *args)
[@b, @a].each do |x|
if x.respond_to?(name)
return x.method(name).call(*args)
end
end
raise "メソッド #{name} が見つかりません"
end
end
c = C.new('A' => '好きな食べ物', 'B' => '好きな飲み物')
c.putsa('みかん')
c.putsb('コーヒー')
c.putsd()
p C.ancestors
# output
好きな食べ物はみかんです
好きな飲み物はコーヒーです
呼び出されたクラスはBです
[C, Object, Kernel, BasicObject]
もう一つのdeletagor
を使用した例です。
メソッド数が多い場合は、こちらの方が楽かもしれません。
メモ
- Python の 多重継承の3 を学習した
- 百里を行く者は九十里を半ばとす