7
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

wxPythonやTkinterで呼び出す __init__ はPythonでの継承元クラスの__init__呼び出しだった

Last updated at Posted at 2017-01-15

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
7
14
4

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
7
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?