良いタイトルが思い浮かばなかったのですが...
以下のソースコードを実行した時に、何がprintされるかを試した時のメモです。
class Parent: def reply(self): self.say() def say(self): print('parent!') class Child1(Parent): def reply(self): super().reply() def say(self): print('child1!') if __name__ == '__main__': c1 = Child1() c1.reply() # => ??
このソースコードの挙動です。
- 子の
reply()
メソッドでは、親のreply()
メソッドを呼ぶ - 親の
reply()
メソッドでは、self.say()
メソッドを呼ぶ
気になる点です。
- 親子どちらの
say()
メソッドが呼ばれるのか?
目次
環境
Python3.6の場合
前述のコードを py3.py
として保存し、実行してみます。
(env36) $ python py3.py child1!
子の say()
メソッドが呼ばれました。
処理順を確認するため、printを仕込んでみます。
class Parent: def reply(self): print('[parent - reply]{}'.format(type(self))) self.say() def say(self): print('[parent - say ]{}'.format(type(self))) print('parent!') class Child1(Parent): def reply(self): print('[child - reply]{}'.format(type(self))) super().reply() def say(self): print('[child - say ]{}'.format(type(self))) print('child1!') if __name__ == '__main__': print('--- parent reply --->') p = Parent() p.reply() print('--- child1 reply --->') c1 = Child1() c1.reply()
実行してみます。
$ python py3.py --- parent reply ---> [parent - reply]<class '__main__.Parent'> [parent - say ]<class '__main__.Parent'> parent! --- child1 reply ---> [child - reply]<class '__main__.Child1'> [parent - reply]<class '__main__.Child1'> [child - say ]<class '__main__.Child1'> child1!
super()で呼んだ親クラスの reply()
メソッドの引数selfに Child1
クラスのインスタンスが渡されています。
これは、Python3の super().reply()
が super(Child1, self).reply()
と同じであり、後者が引数に self
が渡されていることからも分かります。
その結果、Parentクラスで self.say() した時に、Child1クラスの say() メソッドが呼ばれます。
もし、super()を使わない場合は、親クラス.メソッド(引数としてselfを渡す)
という形式、ここでは Parent.reply(self)
とします。
class Child3(Parent): def reply(self): print('[child - reply]{}'.format(type(self))) Parent.reply(self) def say(self): print('[child - say ]{}'.format(type(self))) print('child3!')
実行してみると、super()と同じ結果となりました。
[child - reply]<class '__main__.Child3'> [parent - reply]<class '__main__.Child3'> [child - say ]<class '__main__.Child3'> child3!
Python2.7の場合
Python3のコードのうち、以下のように差し替えます。
- print文へと変更
- 引数なしの
super()
がないため、super(Child1, self)
を使う
class Parent: def reply(self): print '[parent - reply]{}'.format(type(self)) self.say() def say(self): print '[parent - say ]{}'.format(type(self)) print 'parent!' class Child1(Parent): def reply(self): print '[child - reply]{}'.format(type(self)) super(Child1, self).reply() def say(self): print '[child - say ]{}'.format(type(self)) print 'child1!' if __name__ == '__main__': print('--- parent reply --->') p = Parent() p.reply() print('--- child1 reply --->') c1 = Child1() c1.reply()
実行してみます。
--- parent reply ---> [parent - reply]<type 'instance'> [parent - say ]<type 'instance'> parent! --- child1 reply ---> [child - reply]<type 'instance'> ... TypeError: super() argument 1 must be type, not classobj
エラーとなりました。super()のところで例外が起きてるようです。
原因は、Parentクラスの定義 class Parent:
が、Python2の場合 old-style classes
になるためです。
そのため、Python2でも動作させるためには、new-style classes
として、Parentクラスで object
を継承します。
class Parent(object): def reply(self): # あとは同じ
実行結果です。
[child - reply]<class '__main__.Child1'> [parent - reply]<class '__main__.Child1'> [child - say ]<class '__main__.Child1'> child1!
new-style classesのため、 <class '__main__.Child1'>
へと出力が変わりました。
もしくは、 old-sytle classesでも使える、 Parent.reply(self)
にします。
class Child1(Parent): def reply(self): print '[child - reply]{}'.format(type(self)) Parent.reply(self)
実行結果です。
child - reply]<type 'instance'> [parent - reply]<type 'instance'> [child - say ]<type 'instance'> child1!
old-style classesのため、 <type 'instance'>
と出力されています。
ソースコード
GitHubに上げました。
https://github.com/thinkAmi-sandbox/python_misc_samples/tree/master/e.g._call_overrided_method_using_super
参考
- エキスパートPythonプログラミング改訂2版 Michal Jaworski:生活・実用書 | KADOKAWA のp110~p117
- O'Reilly Japan - 初めてのPython 第3版 のp595~596
- O'Reilly Japan - Fluent Python のp373~374