Tkinter, wxPythonのコンストラクタでよく見る定型文
Tkinter, wxPython で、tk.Frame, wx.Panelなど任意のWidgetを継承したクラスを作成するときに、必ずサンプルの中にあり、また自分自身も使用している定型文がある。
class MyTkinterFrame(tk.Frame):
def __init__(self, master = None):
# これ!
tk.Frame.__init__(self, master)
...
class MyWxPanel(wx.Panel):
def __init__(self, parent = None):
# これも!
wx.Frame.__init__(self, master = None):
...
この処理の意味がわからないで、しかしこれを付け加えないとエラーとなるのでとりあえず呪文と思って書いていたけれど、よくよく考えると継承元クラスのコンストラクタを呼んでいるだけだ ということに気づいたのでメモ。
知っている人には当たり前すぎるのではないだろうか。また、知らなくてもとりあえず書いておけば素敵なTkinter, wxPython Life を過ごすことができる。豆知識としてどうぞ。
補足 @ 2017-01-18
コメントいただいたのだが、勘違いをしていた。文中では、__init___
をコンストラクタと呼び、__init__
の呼び出しをコンストラクタの呼び出しと呼んでいる。
が、Python の __init__
は、コンストラクタではない。何かと言われると、きっと初期化処理なんだろう、ぐらいの理解なのが申し訳ないが、、、。
参考:基底クラスの__init__()
について
適切な読み替えがわからなかったので、全文修正はしていない。文中でコンストラクタ、と呼んでいる箇所に関してはよく注意して読んでいただきたい。)
暗黙の引数 self
Pythonではクラスのインスタンスメソッド を呼び出すときに、暗黙の引数 self
が隠れている。次の呼び出しは同じ処理を呼び出すことができる。
class SomeClass:
def SomeMethod(self):
# do something
...
def OtherMethod(self):
self.SomeMethod() # その1
def AnotherMethod(self):
# SomeMethod(self) # その2 <- 追記 : この呼び出しはエラーとなるが、確認不足で書いていたので修正
SomeClass.SomeMethod(self) # その2
ちなみにその2の記法でも、クラスの外側でも呼び出せた。
instance = SomeClass()
instance.SomeMethod() # その1
SomeClass.SomeMethod(instance) # その2
その2の記法はめったに使わない。そして、引数に必要な値を渡しているのだから、当然呼び出せるだろう。**こう書けることに驚くが。**こう書けます!と言われて、そういわれればそうですね!とかしか言いようがない。言語仕様というやつである。Pythonらしさか。
継承先クラスから継承元クラスの __init__
呼び出し
Python では、継承元クラスのコンストラクタは、継承先クラスを暗黙には呼び出してくれない。したがって、継承をするときは明示的に継承元のコンストラクタを呼び出す必要がある。
class MyInherit(MySuper):
def __init__(self):
# 継承元コンストラクタの呼び出し
# 以下の記法は、Python3 でないと使えないとか
super().__init__()
あるいは
class MyInherit(MySuper):
def __init__(self):
# 継承元コンストラクタの呼び出し, Python 2 でもOK
super(MySuper, self).__init__()
ごく個人的な事情だが、めったに継承を使用したプログラミングをしない。ところが、Pythonと Tkinter, wxPythonではごく普通のことのようなので、勉強となった。
(参考 : Pythonのsuper()関数の使い方)
明示的な self の指定と 継承元クラスの init 呼び出しを合わせる
そして問題の定型文である。
継承元クラスの コンストラクタ __init__
呼び出しは、super()
を使用しなくともできる。継承元クラスの__init__
呼び出しとしては、むしろこちらのほうがよく見かけるのかもしれない。self
を明示的に引数として引き渡して呼び出している。
class MyInherit(MySuper):
def __init__(self):
# 継承元コンストラクタの呼び出し
# Python 2, Python 3 の両方で可能
MySuper.__init__(self)
...
なるほどね!
なぜこのことに気づかなかったのか?
慢心と思い込み
今まで自分が最も使用した言語が C# であり、継承元コンストラクタの呼び出し方法が異なっていたため。また、クラスのメソッドというと、<インスタンス変数>.<メソッド>(...)
という呼び出しが当たり前だとどこかで思っていたため
C# の継承元コンストラクタの呼び出しは、コンストラクタの宣言時に行うこととなっている。
class MyInherit : MySuper
{
// コンストラクタ
public MyInherit(int val) : MySuper(val)
{
// do initialize...
}
}
C++やJavaは触ったことがほとんどないものの、同様に暗黙の継承元コンストラクタ呼び出しが行われるようだ。
(参考:C#のコンストラクタとデストラクタ)
まとめ
- Python では、継承元クラスのコンストラクタ
__init__
の明示的な呼び出しが必須である。 - 継承を行う場合の定型文は、明示的な
self
の引数指定によるものである。 - あなたはプログラミング言語ごとの差に少し詳しくなった。
私個人としては、使い方や書き方は覚えておいて、なぜこのような差があるかについては 文化の差 ということにして次に進みたいと思う。
また、super().__init__()
が、可読性や意味の伝わりやすさという観点で優れていると思うが、Python3 からの機能ということであれば、浸透するのはこれからの話となるだろう。積極的に使っていきたい。
確認した環境
- OS : Windows 10
- Python : 3.5.2
- Anaconda にてインストール
- Tkinter : Python 3.5.2付属, バージョン未確認
- wxPython : wxPython-Phoenix 3.0.3.dev2749