次のようなクラスを作りたいと考えたとします。
- 親クラスのクラス変数に
5
を持つ - 親クラスのクラスメソッドでは、親子両方のクラス変数を使って何か適当な計算を行い、その結果を返す
- 適当な計算の中身は、親クラス変数の
5
と子クラス変数のxを足してその和に2
をかける、というものにしました。 - 今回、親クラスのインスタンス化については考えないことにします。
- 適当な計算の中身は、親クラス変数の
まずは親クラスのクラス変数と、そのクラスメソッドを書いてみます。
class Parent:
def __init__(self, child_num):
self._parent_num = 5
self._child_num = child_num
def calc(self):
result = (self._parent_num + self._child_num) * 2
return result
子クラスについても書いてみます。子クラスのクラス変数は仮に3
と4
にしてみましょう。
継承をすると、親クラスと同じメソッドは子クラスで宣言しなくても使えるので、calc
メソッドの定義は必要なさそうです。
しかし、3
と4
については親との差分なので、次のような記述が必要になるかもしれません。
class Child_A(Parent):
def __init__(self):
self._child_num = 3
class Child_B(Parent):
def __init__(self):
self._child_num = 4
ところが、これでChild_A().calc()
を実行するとエラーになってしまいます。
エラーの原因はself._parent_num
が定義されていないというものになるはずです。
親で定義されているはずなのに、定義されていないなんてエラーが出るのはなぜ?
これは、子で改めて__init__
を定義したせいで、親の__init__
が上書きされてしまったからなんですね。
self._parent_num
を定義しているのはあくまでも親の__init__
ですので、子のほうで上書きしてしまうと未定義のままになってしまうわけです。
となると、次のように書くしかないのでしょうか?
class Child_A(Parent):
def __init__(self):
self._parent_num = 5 # この同じ内容を何度も書かなければいけないのか?
self._child_num = 3
class Child_B(Parent):
def __init__(self):
self._parent_num = 5 # この同じ内容を何度も書かなければいけないのか?
self._child_num = 4
この書き方であればエラーは起こらないでしょうが、あまり良い方法とは言えません。
子クラスが何十個も必要になったり、あとからself._parent_num
の値を変えたくなったり、そもそも親クラスのクラス変数がたくさん定義されているような場合になると目がくらんでくるでしょう。
そこで役に立つのがsuper()
というわけです。
公式ドキュメントの記述はこちらになります。
super([type[, object-or-type]])
しかし、公式の内容はあまりに網羅的・あまりに詳細なので、パッと理解するにはいささか複雑すぎます(もちろん、しっかりとした理解を得るためにはそういった記述が必要になります)。
ここでは、現在問題にしているケースでの使い方に絞って見ていくことにします。
super()
とは、少なくともここで問題にしているケースにおいては、親クラスのメソッドを実行してくれるものとして役に立ってくれます。
次のようなクラス定義について考えてみましょう。
class Child_A(Parent):
def __init__(self):
super().__init__(3)
とても不思議なクラス定義が現れました。
しかし、しっかり読んでみれば話は簡単です。
super()
とは、少なくともここでは、親クラスのメソッドを実行してくれるものとして役に立ってくれるものでした。
そのため、子クラスの中で実行したsuper().__init__(3)
というコードは、親クラスのdef __init__(self, child_num)
の引数child_num
に対して3
を渡してインスタンス化するという処理を実行してくれるコードとなっているのです。
結果としてChild_A
クラスの中の__init__
そのものは親クラスとは異なる(上書きされた)内容となっていますが、その中では親クラスの__init__
と全く同じ処理を通ってくれています。
それゆえ、super()
を使わなかった場合に未定義となっていたself._parent_num
もChild_A
の中からしっかりと参照できますし、self._child_num
の値にも3
が代入されている状態で利用することができます。
従って、子を子をいくつも作りたい場合は次のようなコードを書くと幸せになれるでしょう。
class Parent(object): # object型を継承
def __init__(self, child_num):
self._parent_num = 5
self._child_num = child_num
def calc(self):
result = (self._parente_num + self._child_num) * 2
return result
class Child_A(Parent):
def __init__(self):
super().__init__(3)
class Child_B(Parent):
def __init__(self):
super().__init__(4)
おや!注意深い方は、親クラスの方にも何か変更が加えられているのに気が付くでしょう。
実は、super()
を使って親クラスのメソッドを実行したいなら、親クラスはobject
型を継承している必要があります。
最初のコードではobject
型を継承するように書いていませんでした。例えの説明に必要なかったので。
実装してみるときはお気を付けください。それでは。