質問: ### super()の動き(仕様)を説明してほしいです。 ### 経緯 多重継承すると、継承順で一番先にメソッドを定義している スーパークラスのメソッドしか呼び出されないので、 継承しているクラスすべてのオーバーライドしているメソッドを呼び出す方法を調べたら super()を下記のように使えばいいということがわかりましたが、いまいちsuper()の仕様がわかりません。 試してみたコード -------- ``` class greeting: country=None def morning(self):pass class jp_greeting(greeting): def __init__(self): self.country="japan" def morning(self): print("おはよう.") super(jp_greeting,self).morning() class en_greeting(greeting): def __init__(self): self.country="america" def morning(self): print("good morning.") super(en_greeting,self).morning() class de_greeting(greeting): def __init__(self): super(de_greeting,self).country="germany" def morning(self): print("guten tag.") class myGreeting(jp_greeting,en_greeting,de_greeting): def __init__(self): self.country="" def morning(self): super(myGreeting,self).morning() myGreeting().mornig() ``` 試したこと ----- 1. 最後の呼び出しを`super(myGreeting,myGreeting).mornig(myGreeting())`にしても、得られる結果は同じでした。 2. en_greettingクラスの`super(en_greeting,self).morning()`を`super(jp_greeting,self).morning()`にしたら永続ループしました。 3. 今度は`super(de_greeting,self).morning()`に変えたら、de_greetingのmorning()は呼び出されませんでした。 ### 予想 uper(superClass,class)はclassでsuperClassの次に継承されているクラスを指定する。 だから、superClassはメソッドを呼び出しているクラスにすると継承した順に呼び出される。 でも、これでは1.の動きを説明できません。
回答: > > 予想 > > > super(superClass,class)はclassで**superClassの次に継承されているクラス**を指定する。 > > だから、superClassはメソッドを呼び出しているクラスにすると継承した順に呼び出される。 > > > 継承した順が「method resolution order(以下MROと略記)の順番」という意味と捉えるなら考え方自体は概ねあっているといえるでしょう。しかし > > でも、これでは1.の動きを説明できません。 > > > と言っておられることから`super`の引数についての**正確な仕様を見落としておられる**ようです。正確な仕様は仕様書を見て把握しましょう。特に`super`のように若干トリッキーなものだと仕様書をみずに「例だけ見て正確な仕様を掴もうとする」のは難しいと思います。 (1) Python公式ドキュメント Python 標準ライブラリ > 組み込み関数 > super([*type*[, *object-or-type*]]) (2) オンラインヘルプ `help(super)`およびIPython上での`super?` Pythonではクラスも型も同義ですが以下クラスと表記します。 myGreetingのMROは myGreeting jp_greeting en_greeting de_greeting greeting object のようになります。これは`myGreeting.__mro__`を見ると確認できます。これについては質問者さんも把握しておられると思います。 さて、質問者さんの予測に不足している点はsuperの第二引数はクラスではなくてもよく任意のオブジェクト(なんらかのクラスのインスタンス)が指定できることです。クラスでないものを指定した場合、そのオブジェクトのクラスがMROを決めます。 それゆえ `super(myGreeting, myGreeting).greeting(...)` と、(myGreetingのインスタンスである)selfを用いて `super(myGreeting, self).greeting(...)` とした場合のどちらでも同じMROとなるので`.greeting(...)`がjp_greeting.greetingになるのです。 ひょっとすると質問者さんの疑問は `super(myGreeting, myGreeting).greeting()` `super(myGreeting, self).greeting(self)` のようにgreetingの引数にselfの指定有無がかわる点かも知れませんが、それはsuperの仕様というよりはPythonのメソッドの仕組みで用いられているbounded methodと同様の話と考えるとよいと思います。質問者さんの疑問がここにあるかどうかはっきりしないので、正確な動作を長々説明するのを止して大雑把な説明をしますと、 例1: ``` class A: def m(self): pass a = A() a.m() # このように記述すると第一引数は指定しなくてもaになる A.m(a) # このように記述すると第一引数を明示しなくてはならなくなる ``` 上で`a.m`の結果はbounded methodとなりメソッドのレシーバーが`a`の値であることをbounded methodオブジェクト自体が記憶しています。一方`A.m`と記述すると`A.m`の結果は単なる関数であり、mを定義したときの仮引数の通りに実引数を渡さないといけないことになります。 `super(aClass, self).m`とすると解決されたメソッドmがbounded methodとして返され `super(aClass, selfClass).m`とすると解決されたメソッドは関数として返されます。要は例1と同じことと捉えることができるわけです。 追記: 前述のsuperの大雑把な説明はインスタンスメソッド内においてのものです。classmethodでもそうあってほしいとおりに動作しますが、staticmethodではsuperの引数を省略できず`super(__class__, __class__)`などと書かねばなりません。