Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonの継承を完全解説:親クラス・子クラスの基本からメソッドのオーバーライドまで

Pythonの継承(inheritance)とは、あるクラス(子クラス)が別のクラス(親クラス)のコードを受け継いで利用できるようにする仕組みです。継承はPythonのようなオブジェクト指向言語において不可欠な機能であり、コードの重複を減らし、開発効率を大幅に向上させます。


Pythonはオブジェクト指向言語であり、再利用可能なコードブロックを作成することで、より効率的なプログラミングを実現できます。その中心的な手法のひとつが「継承」です。継承を使えば、あるサブクラスが別のクラスのコードを参照・利用できるようになります。

たとえば、「銀行口座」を管理するクラスがあり、そこから「子供用銀行口座」というサブクラスを作りたいとします。このとき、既存の「銀行口座」クラスの属性をそのまま引き継げば、同じコードを何度も書く必要はありません。

本記事では、Pythonにおける継承の基礎を丁寧に解説します。親クラスと子クラスの仕組みや、属性・メソッドのオーバーライド(上書き)の方法まで、実例を交えて学んでいきましょう。

Pythonにおける継承とは

継承とは、あるPythonクラスが別のクラスの中で定義される仕組みを指します。サブクラス(subclasses)または子クラス(child classes)と呼ばれるクラスは、親クラス(parent classes)から値や機能を受け継ぐことができます。これは現実世界で子供が親から特徴を受け継ぐのに似ています。

継承が便利な理由は、親クラスと同じ型の値を持つサブクラスを、同じ宣言を繰り返すことなく作成できる点にあります。

親クラス(Parent Class)の作成

親クラス(基底クラス(base classes)とも呼ばれます)は、サブクラスがアクセスできる「マスターとなる構造」を作成します。親クラスを一度定義すれば、共通の値を何度も宣言し直すことなく、複数のサブクラスを作成できます。

具体例で見てみましょう。「大人用口座」と「子供用口座」という2種類の銀行口座を扱うプログラムを作るとします。

どちらの口座も「銀行口座」である以上、共通する項目が多くあります。しかし、それぞれ固有の情報も必要です。たとえば、大人の口座には当座貸越(オーバードラフト)機能がある一方、子供の口座では残高の範囲内でしか取引できない、といった具合です。

そこで、まず銀行口座の親クラスを定義し、その後に大人用・子供用の子クラスを作成する構成にします。

以下は、銀行口座の親クラスを宣言する例です。

class BankAccount:
    def __init__(self, forename, surname, balance):
        self.forename = forename
        self.surname = surname
        self.balance = balance

ここではBankAccountクラスを作成し、名前(forename)、姓(surname)、残高(balance)という3つの値を格納しています。さらに、クラスにメソッドを追加してみましょう。

class BankAccount:
    def __init__(self, forename, surname, balance):
        self.forename = forename
        self.surname = surname
        self.balance = balance

    def getDetails(self):
        print("Forename: ", self.forename)
        print("Surname: ", self.surname)

    def getBalance(self):
        print("Balance: $ ", self.balance)

これで、クラスは3つの値に加えて2つの新しいメソッドを持つようになりました。getDetails()メソッドは口座名義人の名前と姓を表示し、getBalance()メソッドは残高を返します。

BankAccountクラス内で宣言されたすべてのメソッドと変数は、後ほど作成する子クラスからもアクセス可能になります。

実際に動かしてみましょう。次のコードでサンプルの口座を作成できます。

account = BankAccount("John", "Appleseed", 100)

これにより、100ドルを保有するJohn Appleseedさんの口座が作成されました。あとは通常のクラスと同じように参照できます。getBalance()メソッドを実行して残高を確認してみます。

print(account.getBalance())

プログラムは次のように出力します。

Balance: $ 100

子クラス(Child Class)の作成

先ほどの例では、銀行口座情報を格納する親クラスを定義しました。しかし、「18歳未満向けの子供用口座」のように特定の種類の口座情報を保存したい場合はどうすればよいのでしょうか。そこで登場するのが子クラスです。

子クラス(サブクラス)は、親クラスから値を継承します。つまり、各子クラスは親クラスで宣言したメソッドや変数を参照できるのです。

ここで、ChildBankAccountという子クラスを作成するとしましょう。この口座では、ユーザーの氏名や残高といった口座情報を返せることに加え、restricted(制限あり)という新しい値を持たせます。18歳未満のユーザーは完全な銀行口座を開設できないため、このフラグが必要になるわけです。

子クラスを作成するには、次のようなコードを使います。

class ChildBankAccount(BankAccount):

ChildBankAccountクラスは、括弧内にBankAccountを指定することで、その子クラスになります。もし子クラスを親クラスとまったく同じ内容にしたい場合は、passキーワードを使えます。

class ChildBankAccount(BankAccount):
    pass

しかし前述のとおり、今回の子クラスには特別な値——アカウントが制限されていることを示す変数——が必要です。

そこで、子クラスにrestrictedという新しい値を追加しましょう。

class ChildBankAccount(BankAccount):
    def __init__(self, forename, surname, balance, restricted=True):
        self.forename = forename
        self.surname = surname
        self.balance = balance
        self.restricted = restricted

    def isRestricted(self):
        print("This account is restricted as the user is under 18.")

コードを分解して確認しましょう。

上記のコードでは、ChildBankAccountが4つの値(forename、surname、balance、restricted)を持つことを宣言しています。

最初の3つは親クラスと同じ値ですが、restrictedは新しく、子クラス限定の値です。デフォルトではrestrictedTrueに設定されます。

つまり、標準のBankAccountChildBankAccountではない)を作成した場合、restrictedの値にはアクセスできません。同様に、isRestricted()メソッドもChildBankAccountを作成しない限り呼び出せません。

ChildBankAccountを作成し、getBalance()isRestricted()メソッドを使ってクラスの動作を確認してみましょう。

child_account = ChildBankAccount("Bill", "Appleseed", 100)
print(child_account.getBalance())
print(child_account.isRestricted())

コードの出力は次のとおりです。

Balance: $ 100
This account is restricted as the user is under 18.

ご覧のとおり、プログラムはまず100ドルを保有するBill Appleseedさんの新しいChildBankAccountを定義します。続いてgetBalance()メソッドを実行して残高を返します。注目すべきは、getBalance()は親クラスBankAccountで宣言されていますが、継承によってChildBankAccountからもアクセスできるという点です。

また、プログラムはisRestricted()メソッドも実行し、18歳未満のため口座に制限がかかっていることを伝えます。このメソッドはChildBankAccount内で宣言されているため、親クラスBankAccountからはアクセスできません。もし制限付きのBankAccountを作りたい場合は、親クラス側を変更する必要があります。

親クラスのメソッドのオーバーライド(上書き)

これまでの例では、銀行口座情報を格納する親クラスBankAccountと、18歳未満の口座保持者向けの子クラスChildBankAccountを作成しました。子クラスはBankAccountのメソッドを継承しつつ、独自のisRestricted()メソッドも持っています。

では、既存の親クラスのメソッドを変更したい場合はどうすればよいでしょうか。たとえば、BankAccountの残高照会時に「当座貸越をご利用いただけます(You are eligible for an overdraft)」というメッセージを表示したいが、ChildBankAccountの利用者には表示したくないとします。

これを実現するには、親クラスのメソッドをオーバーライドします。

まず、残高照会時にオーバードラフトの案内メッセージを表示するよう修正した親クラスのコードを見てみましょう。

class BankAccount:
    def __init__(self, forename, surname, balance):
        self.forename = forename
        self.surname = surname
        self.balance = balance

    def getDetails(self):
        print("Forename: ", self.forename)
        print("Surname: ", self.surname)

    def getBalance(self):
        print("Balance: $ ", self.balance)
        print("You are eligible for an overdraft.")

この状態でJohn Appleseedさんの新しいBankAccountを作成して残高を表示すると、オーバードラフトの案内メッセージも一緒に出力されます。

account = BankAccount("John", "Appleseed", 100)
print(account.getBalance())

出力結果:

Balance: $ 100
You are eligible for an overdraft.

しかし、この変更により、BankAccountから値を継承しているChildBankAccountでも同じメッセージが表示されてしまいます。実際に確認してみましょう。

child_account = ChildBankAccount("Bill", "Appleseed", 100)
print(child_account.getBalance())

出力結果:

Balance: $ 100
You are eligible for an overdraft.

一般的に子供はオーバードラフトを利用できないため、コードを修正する必要があります。そのために、ChildBankAccountクラスを変更し、新しいgetBalance()関数を宣言します。

現在のChildBankAccountのコードは次のとおりです。

class ChildBankAccount(BankAccount):
    def __init__(self, forename, surname, balance, restricted=True):
        self.forename = forename
        self.surname = surname
        self.balance = balance
        self.restricted = restricted

    def isRestricted(self):
        print("This account is restricted as the user is under 18.")

このコードに、新しいgetBalance()関数を追加します。この関数は親クラスで宣言したものと同じように動作しますが、オーバードラフトに関するメッセージは含みません。追加するコードは次のとおりです。

...

def getBalance(self):
    print("Balance: $ ", self.balance)

それでは、子供の口座残高を取得してみましょう。

child_account = ChildBankAccount("Bill", "Appleseed", 100)
print(child_account.getBalance())

出力結果:

Balance: $ 100

この例では、ChildBankAccountの子クラス内でgetBalance()メソッドを上書き(オーバーライド)しました。新しいgetBalance()メソッドはユーザーの残高のみを表示します。一方、親クラスBankAccountgetBalance()メソッドは、残高に加えてオーバードラフトの案内メッセージも表示します。

親メソッドのオーバーライドは、複数の子クラスが親と似たメソッドを使いつつも、それぞれ固有の変更を加えたい場合に非常に有効です。今回のケースでは、大人の口座にはオーバードラフトのメッセージを表示し、子供の口座には表示しないため、ChildBankAccount内で親メソッドをオーバーライドしたのです。

まとめ

継承は、オブジェクト指向プログラミングにおいて、親クラスで既に宣言済みの値を格納できるサブクラスを作成するための仕組みです。似たような値を扱うクラスを複数作成する際、継承を活用すれば同じコードを何度も書き直す必要がなくなります。

本記事では、Pythonにおける継承の役割を探り、継承と親クラス・子クラスの仕組みを解説したうえで、親メソッドのオーバーライド方法まで学びました。これであなたも、Pythonのクラスと継承をエキスパートのように扱えるはずです!

  1. Pythonインタープリターとは?仕組みと初心者向けオンライン環境を徹底解説

    Pythonは学びやすいプログラミング言語として知られています。その理由のひとつが、自分のマシンに正しいバージョンのPythonをインストールしなくても、オンラインで利用できるPythonインタープリターが数多く存在する点です。本記事では、Pythonインタープリターの仕組みについて解説し、初心者にも使いやすい人気のオンラインインタープリターをご紹介します。 Pythonプログラムはどのように実行されるのか? PythonはJavaと同様に、純粋なインタプリタ言語ともコンパイラ言語とも分類できず、両方の性質を併せ持っています。 ここでいう「コンパイル」とは、特定のプラットフォームに依存し

  2. Pythonの継承とは?単一継承と階層継承の基本をサンプルコードで解説

    本記事では、Python 3.xにおける継承(インヘリタンス)とクラスの拡張方法について詳しく解説します。 継承とは、現実世界のモノや概念の関係性を自然に表現できる、オブジェクト指向プログラミングの中核となる仕組みです。継承を活用すると、次のようなメリットが得られます。 再利用性:すでに書いたコードを流用でき、重複を削減できる 推移性:クラス間の関係を連鎖的に引き継げる 開発速度の向上:ゼロから書かずに済むため、短期間で開発できる 保守性・拡張性:既存クラスを壊さずに機能を追加しやすい 継承の5つの種類 Pythonの継承は、その構造によって主に以下の5種類に分類されます。 単一継承(