Python オーバーライドとsuper()

Python学習【365日チャレンジ!】80日目のマスターU(@Udemy11)です。

新年度が始まった4月も今日で最後になりましたが、4月の終わりに、切りの良いPython学習80日目を迎えられたのはなかなか気分がいいもんです。

とはいえ、いまの世の中はまさに、自宅にいることが求められている時期ですので、少し気分は沈みがちになりますが、逆に時間を有効に使えるチャンスと考えて、Udemyの講座を受講し、スキルアップを図りましょう。

こんなときだからこそ時間を有効活用して、自分のスキルを磨くか、永遠とYoutubeループを見つづけて無駄な時間を過ごすか、あなたの行動次第であなたの未来が大きく変わります。

この大変な時期をチャンスに変えるために、いますぐUdemyを活用しましょう!

↓公式サイトはこちら↓
UdemyセールUdemyセール

それでは、今日もPython学習をはじめましょう!

昨日の復習

昨日は、クラスの継承について学習しました。

class Car:
    def drive(self):
        print('manual drive')

class ToyotaCar(Car):
    pass

toyota_car = ToyotaCar()
toyota_car.drive()

出力結果

manual drive

Carをベースクラスとして、5行目でToyotaCarクラスがCarを継承することで、Carに定義されたdriveメソッドを使うことができました。

ちなみに、passは何もしないという意味でしたね。

クラスの継承の詳細については、昨日の記事を参考にしてください。

それでは今日の学習内容にはいりましょう。

メソッドのオーバーライド

メソッドのオーバーライドとは、ベースクラスで定義したメソッドを継承先のクラスで上書き処理をすることです。

class Car:
    def drive(self):
        print('manual drive')

class ToyotaCar(Car):
    def drive(self)
        print('semi auto drive')

toyota_car = ToyotaCar()
toyota_car.drive()

出力結果

semi auto drive

ToyotaCarは、Carを継承しているので、driveメソッドは、manual driveですが、7行目のオーバーライドによって、driveメソッドがsemi auto driveに上書きされます。

これは、引数のデフォルト値を入れているけど実行時に引数の値を入れるのと似ていますね。

ベースクラスで定義しているので、継承先のクラスでは記述の必要はないが、オーバーライドによって上書きが可能ということです。

super()の活用

続いて、コンストラクタで指定してしたベースクラスの引数をsuper()を使ってオーバーライドしてみます。

class Car:
    def __init__(self, name=None):
        self.name = name

class ToyotaCar(Car):
    def __init__(self, name='Crown', auto_drive=False):
        super().__init__(name)
        self.auto_drive = auto_drive

toyota_car = ToyotaCar()
print('Name:', toyota_car.name)
print('Auto Drive:', toyota_car.auto_drive)

出力結果

Name: Crown
Auto Drive: False

super()は、ベースクラスのことを指すので、7行目のsuper().__init__(name)は、Carnameということです。

つまり、NoneだったnameCrownに上書きするために、super()を使っているということです。

super()を使う理由

今紹介した使い方の場合、7行目をself.name = nameと記述しても問題ないんじゃないの?と思った方もいるかもしれません。

わたしもそう思いました。

なぜsuper()を使ってベースクラスを参照するのか不思議ですよね。

その答えは、オーバーライドにあって、クラスを継承した先で__init__でコンストラクタを定義すると、すべて上書きされるので、ベースクラスで定義した処理がなくなってしまいます。

たとえば、

class Car:
    def __init__(self, name=None):
        self.name = name
        print(1111)
        print(12345)
        print(133221)

このように数字を出力する処理をしていた場合(こんな処理はまずありえませんが例として)、クラスを継承したToyotaCarで、コンストラクタを記述していなければ、ToyotaCarのインスタンスを作った際に、4行目から6行目の数字が出力されます。

しかし、次のように__init__をオーバーライドしてnameを変更すると、__init__がまるごと上書きされるので、ベースクラスの4行目から6行目が削除されてしまい、数字が出力されません。

class ToyotaCar(Car):
    def __init__(self, name='Crown')
        self.name = name
    # ベースクラスの数字の出力がない__init__に上書きされる

super()を使えば、__init__の一部分だけを指定して変更することができるわけです。

つまり、次のような記述にすれば、ToyotaCarのインスタンスを作成したときに、初期化で4行目から6行目の数字が出力されます。

class Car:
    def __init__(self, name=None):
        self.name = name
        print(1111)
        print(12345)
        print(133221)

class ToyotaCar(Car):
    def __init__(self, name='Crown'):
        super().__init__(name)

toyota_car = ToyotaCar()

出力結果

1111
12345
133221

ほんとややこしいですが、10行目のsuper().__init__(name)は、ベースクラスの__init__の第一引数(name)に、新しいnameを代入していることを理解しておきましょう。

まるでパズル

引数が絡んできて、一体どの値を変更しているのかわけが分からなくなってしまいます。

今回も記事を書きながら、どのnameを指定しているのか、名前を変えながら、いろいろと試してようやく理解できたのですが、最初はなにがなんやらよくわかりませんでした。

なんとなく聞いていれば、わかった気になってしまうのですが、実際にコードを理解するには一つ一ついじりながら変更してみてはじめて分かることがたくさんあります。

コードをそのまま写経するのも勉強になりますが、ちょっと変えてみたり、追加したりすることで、どのように動いているのかがより理解できるようになります。

ぜひ、いろいろと試してみてください。

それでは、明日もGood Python!