LoginSignup
0
0

More than 1 year has passed since last update.

[py2rb] 多重継承の6

Last updated at Posted at 2022-02-25

はじめに

移植やってます。
( from python 3.7 to ruby 2.7 )

多重継承 (Python)

前回の記事のコメントにて、@Nabetani さんよりRuby3.1ではクラス変数のオーバーライドがエラーになることを教えていただきました。

親クラスに、子クラスで既に定義されている同名のクラス変数を追加した場合、子クラスが、そのクラス変数を参照した際に例外 RuntimeError が発生します。

また、クラス変数について興味深い記事を投稿されています。

失敗(Ruby)

module Base
  def __init__
    @name = 'Base'
    @base = 'Base'
    puts 'Base'
  end
end

module C4
  include Base
  def __init__
    @name = 'C4'
    puts 'C4'
    super
  end
end
      
module C2
  include C4
  def __init__
    @name = 'C2'
    puts 'C2'
    super
  end
end

module C3
  include Base
  def __init__
    @name = 'C3'
    puts 'C3'
    super
  end
end

class C1
  include C2, C3
  attr_reader :name, :base

  def initialize
    __init__
  end

  def __init__
    @name = 'C1'
    puts 'C1'
    super
  end
end

c = C1.new
p [c.name, c.base]
p C1.ancestors

# output
C1
C2
C4
C3
Base
["Base", "Base"]
[C1, C2, C4, C3, Base, Object, Kernel, BasicObject]

継承チェーンでBaseC3の後から呼ばれています、よしよし。
20220225a.png

クラス変数からインスタンス変数に替えましたが、@nameが継承元で'Base'に書き換えられています、ぐぬぬ。

成功(Ruby)

module Base
  def __init__
    @name = 'Base'
    @base = 'Base'
    puts 'Base'
  end
end

module C4
  include Base
  def __init__
    puts 'C4'
    super
    @name = 'C4'
  end
end
      
module C2
  include C4
  def __init__
    puts 'C2'
    super
    @name = 'C2'
  end
end

module C3
  include Base
  def __init__
    puts 'C3'
    super
    @name = 'C3'
  end
end

class C1
  include C2, C3
  attr_reader :name, :base

  def initialize
    __init__
  end

  def __init__
    puts 'C1'
    super
    @name = 'C1'
  end
end

c = C1.new
p [c.name, c.base]
p C1.ancestors

# output
C1
C2
C4
C3
Base
["C1", "Base"]
[C1, C2, C4, C3, Base, Object, Kernel, BasicObject]

superの後でインスタンス変数を初期化することにより、正しいオーバーライドになるようです。

継承の実験

module C4
  include Base
  def __init__
    puts 'C4'
-    super
    @name = 'C4'
  end
end

# output
C1
C2
C4
["C1", nil]
[C1, C2, C4, C3, Base, Object, Kernel, BasicObject]

仮に、成功(Ruby)のコードで、module C4superを削除した場合、継承チェーンのC4の次のC3def __init__が呼ばれないためputs 'C3'が実行されないことが分かります。
また、その結果Basedef __init__も実行されないため、@baseが初期化されていないこともわかります。

追記 成功(Ruby nilガード)

module Base
  def __init__
    puts 'Base'
    @name ||= 'Base'
    @base ||= 'Base'
  end
end

module C4
  include Base
  def __init__
    puts 'C4'
    @base ||= 'newBase'
    super
  end
end
      
module C2
  include C4
  def __init__
    puts 'C2'
    super
  end
end

module C3
  include Base
  def __init__
    puts 'C3'
    super
  end
end

class C1
  include C2, C3
  attr_reader :name, :base

  def initialize(name = nil)
    @name = name || 'C1'
    __init__
  end

  def __init__
    puts 'C1'
    super
  end
end

c = C1.new("newC1")
p [c.name, c.base]
p C1.ancestors

# output
C1
C2
C4
C3
Base
["newC1", "newBase"]
[C1, C2, C4, C3, Base, Object, Kernel, BasicObject]

お馴染みnilガード ||=module内で使用してみました。
これだとsuperのタイミングを気にしなくてよさそう。

メモ

  • Python の 多重継承の6 を学習した
  • 百里を行く者は九十里を半ばとす
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0