このページで解説している内容は、以下の YouTube 動画の解説で見ることができます。

【Python入門】既存のメソッドのオーバーライド

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

 クラスを継承して機能を拡張するとき、基底クラスから受け継いだメソッドをそのまま使わずに、派生クラス側で再定義したい場合があります。これをメソッドのオーバーライドと呼びます。オーバーライドを利用すると、既存のメソッド名を引き継ぎつつ、動作だけを派生クラスで自由に変更できます。
 ここでは、オーバーライドの仕組みと、継承したメソッドを活かしつつ追加処理を行うためのsuper関数の使い方について解説します。

プログラムのダウンロード

 「ダウンロード」から、JupyterLab で実行できるサンプルプログラムがダウンロードできます。ファイルは、ESET Endpoint Securityでウイルスチェックをしておりますが、ダウンロードとプログラムの実行は自己責任でお願いいたします。

1.メソッドのオーバーライドとは

1.1. オーバーライドの基本

 基底クラスで定義されているメソッドと同じ名前のメソッドを、派生クラスで再定義(上書き)することをオーバーライドと呼びます。たとえば、基底クラスに show() というメソッドがある場合、派生クラスでも同名の show() を定義すると、派生クラスのオブジェクトから呼び出されるのは基底クラスのものではなく、派生クラスで再定義した show() になります。これにより、クラスごとに異なる振る舞いを実現できます。

1.2. オーバーライドの構文

・オーバーライド
class 基底クラス名:
    def メソッド名(引数, …):  # (A) 基底クラスのメソッド
        文…

class 派生クラス名(基底クラス名):
    def メソッド名(引数, …):  # (B) 基底クラスのものと同名のメソッド
        文…
  • 派生クラスのオブジェクトから メッソッド名() を呼ぶと、(B) が実行される。
  • (A) の基底クラスのメソッドは呼ばれなくなる点に注意

2.super関数を使った基底クラスメソッドの呼び出し

2.1. なぜsuper関数が必要か

 オーバーライドすると、同名メソッドは派生クラスで再定義されたものが優先されます。そのため、派生クラスのメソッド内で基底クラスのメソッドも活用したいときは、適切に呼び出す必要があります。もし self.メッソッド名() のように同名メソッドを再度呼び出すと、再帰的に同じメソッドを呼び出してしまい、RecursionError(再帰エラー)が発生してしまいます。

2.2. super関数の活用

 基底クラスのメソッドを呼び出す場合、super() を使うのが一般的です。これにより、クラス名をハードコーディングせずに基底クラスのメソッドを実行できます。

super().メソッド名(引数, …)

上記の記法を使うと、クラス名を変更してもコードを修正しなくて済むという利点があります。

3.具体例:クラスを置き換えたオーバーライドとsuperの使い方

 ここでは、メディア(コンテンツ)を表す基底クラス Content と、そこから派生した BookMovie クラスを例に示します。それぞれに固有の属性を追加し、__init__ メソッドと show メソッドをオーバーライドします。

class Content:
    # 基底クラス
    def __init__(self, title, price):
        self.title = title
        self.price = price

    def show(self):
        print("Title:", self.title)
        print("Price:", self.price)

class Book(Content):
    # 派生クラス1
    def __init__(self, title, price, author):
        # 基底クラスの __init__ を呼び出して共通の初期化を行う
        super().__init__(title, price)
        # Book 独自の属性を初期化
        self.author = author

    def show(self):
        # 基底クラスの show() を呼び出して共通部分を表示
        super().show()
        # Book 独自の情報を追加表示
        print("Author:", self.author)

class Movie(Content):
    # 派生クラス2
    def __init__(self, title, price, director):
        super().__init__(title, price)
        self.director = director

    def show(self):
        super().show()
        print("Director:", self.director)

# オブジェクトを生成して動作確認
b = Book("Python Best Practices", 2000, "Alice")
b.show()
print()
m = Movie("AI Revolution", 1500, "Bob")
m.show()

実行結果

Title: Python Best Practices
Price: 2000
Author: Alice

Title: AI Revolution
Price: 1500
Director: Bob

詳しい解説

  • Contentクラス(基底クラス)
    __init__titleprice を初期化
    show メソッドでタイトルと価格を表示
  • Bookクラス(派生クラス)
    __init__ でまず super().__init__(title, price) を呼び出し、基底クラスの初期化を使い回し。
    ・さらに、author のような独自の属性を追加
    show をオーバーライドして、基底クラスの show を呼び出した後に author 情報を表示
  • Movieクラス(派生クラス)
    ・Bookクラスと同様に director の初期化と show のオーバーライドを行う。

 実行結果としては、以下のように基底クラスで定義された表示内容と派生クラスの独自内容が両方表示されます。

Title: Python Best Practices
Price: 2000
Author: Alice

Title: AI Revolution
Price: 1500
Director: Bob

4.super以外の呼び出し方

4.1. クラス名.メソッド名(self, 引数, …)

 基底クラスを明示的に書いて呼び出す方法もあります。この場合、クラス名が変わるとコードも修正しなくてはならないため、通常は super を使うのが推奨されます。

class Book(Content):
    def __init__(self, title, price, author):
        Content.__init__(self, title, price)  # self を明示的に指定
        self.author = author

4.2. 再帰の注意

 self.show() のように、自分自身のメソッドを再帰的に呼び出してしまうと、無限ループに陥りRecursionErrorが発生します。基底クラスのメソッドを呼び出したいときは、必ず super() もしくは クラス名.メソッド名(self, ...) を使いましょう。

まとめ

  • オーバーライド: 同名メソッドを派生クラスで再定義することで、継承元と異なる動作を実装できる。
  • super()関数: 派生クラスのメソッド内で、基底クラスのメソッドを呼び出す標準的な方法。再利用と保守性の観点で推奨される。
  • 再帰が発生しないよう、メソッド名が同じときは self.method() ではなく super().method() を使う。
  • クラス名を直接書いて呼び出す方法もあるが、クラス名変更時に修正が必要になるなどデメリットもある。

 オーバーライドを理解すると、必要なところだけ個別に上書きして使うというオブジェクト指向の利点を最大限に活かせます。複数の派生クラスがある場合も、共通部分は基底クラスに集約し、派生クラスごとに追加の処理や振る舞いを実装することで、コードが読みやすく、変更に強い設計を実現できます。