順番に読み進めながら学べます

クラスの継承 — 既存クラスの機能を引き継ぐ

Pythonのクラスの継承をPerson/Employeeで解説。class子(親): で属性とメソッドを引き継ぐ書き方、同名メソッドの上書き(オーバーライド)、super().__init__で属性を追加するパターンを確認できます。

前回__add____str__といった特殊メソッドで、自作クラスに組み込み操作を教える書き方を整理しました。今回はオブジェクト指向の重要な要素の一つである継承について解説します。

なぜ継承を使うのか

アプリを作っていると「ほとんど同じだけど、一部だけ違う」クラスを複数用意したくなる場面が出てきます。たとえば「お客様(Customer)」と「従業員(Employee)」と「管理者(Admin)」のように、nameemailを持ってgreeting()を返す部分は共通で、それぞれ独自の属性やメソッドだけが追加で要る、というケースです。

3 つのクラスにそれぞれ同じname / email / greeting()を書いてもいいのですが、全く同じコードが重複してしまい、修正が入るたびに 3 箇所を直すことになります。共通部分を親クラスにまとめ、各クラスは「親クラスを引き継ぎつつ、差分だけ書く」 ようにすればこの問題を解決できます。これが 継承(inheritance) です。

共通部分を親クラスに集約する
Person(親クラス)name / agegreeting()Employee(子クラス)継承
共通する属性・メソッドは親クラス(Person)に集めて、差分だけを子クラス(Employee)に書く。name / greetingを毎回書き直す必要がなくなる。

親クラスを継承する — class 子(親):

継承の書き方はシンプルです。子クラスを定義するときに、クラス名のあとに丸括弧で親クラスを指定します。これで、子クラスは親クラスが持つ属性とメソッドをそのまま引き継ぐことができます。

以下は、name / ageを持って自己紹介するPersonクラスを親、それを継承するEmployeeを子クラスにした例です。Employee側にはメソッドを 1 つも書いていませんが、親の greeting() がそのまま呼び出せているのが分かります。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age  = age

    def greeting(self):
        print(f"Hello, {self.name}")

    def say_age(self):
        print(f"{self.age} years old")


class Employee(Person):       # ← Person を継承
    pass                       # 中身は空でもよい


e = Employee("田中", 45)
e.greeting()                  # Hello, 田中   ← 親のメソッドが呼べる
e.say_age()                   # 45 years old
親が持つ機能はそのまま子で使える
Person(親クラス)
  • 属性: name / age
  • メソッド: greeting() / say_age()
  • __init__(self, name, age)
Employee(子クラス)
  • 中身は 空(pass)
  • → 親の属性・メソッドをそのまま引き継ぐ
  • Employee("田中", 45) で親の __init__ が呼ばれる
子クラスが空でも、親が持つすべてが使える。共通の挙動を書き直さずに流用できるのが継承の最大の利点。

親クラスPersonを作り、それを継承するだけのEmployeeを定義します。

class Person:を定義し、__init__(self, name, age)self.name / self.ageを代入してください。

Personの中にgreeting(self)を定義し、print(f"Hello, {self.name}")を実行してください。

class Employee(Person):と書いて、本体はpassだけにしてください。

e = Employee("田中", 45)を作り、e.greeting()を実行してください。

(正しく実行できれば解説が表示されます)

Python エディタ

コードを実行してください

メソッドのオーバーライド — 同じ名前で書き換える

継承するだけだと親の振る舞いそのままですが、子クラス側で親と同じ名前のメソッドを書くと、子クラスのほうが優先的に呼ばれます。これがオーバーライド(override)です。

たとえばEmployee用の挨拶を「Hello, Employee 田中」に変えたいときは、子クラスでgreetingを再定義します。Personインスタンスから呼べば親の挨拶、Employeeインスタンスから呼べば子の挨拶 — というふうに、呼び出すインスタンスがどちらのクラスかによって振る舞いが切り替わります。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age  = age

    def greeting(self):
        print(f"Hello, {self.name}")


class Employee(Person):
    def greeting(self):                # ← 同じ名前で再定義 = オーバーライド
        print(f"Hello, Employee {self.name}")


p = Person("佐藤", 30)
e = Employee("田中", 45)
p.greeting()    # Hello, 佐藤
e.greeting()    # Hello, Employee 田中    ← 子クラスのほうが呼ばれる
メソッドを呼んだときの探索順
e.greeting()Employeeに greeting?あり→ 実行Personに greeting?あれば実行なければエラー①探すYesNo時
Python はまず子クラスにメソッドがあるか探し、無ければ親クラスに探しに行く。同名なら子のほうが優先される — これがオーバーライド。

前のコードにgreetingのオーバーライドを足します。

① 前回と同じPersonクラスを作ってください(__init__greeting)。

class Employee(Person):を定義し、その中でgreeting(self)を再定義してprint(f"Hello, Employee {self.name}")を実行してください。

Person("佐藤", 30)Employee("田中", 45)をそれぞれ作り、両方のgreeting()を呼んで結果を比べてください。

Python エディタ

コードを実行してください

super() で親クラスのメソッドを呼ぶ

オーバーライドしつつも、親の処理は活かしてその上に追加で書きたい — そんな場面ではsuper()を使います。super().method()と書くと、今のクラスの親クラスにある同名メソッドを呼ぶことができます。

たとえば「親のgreetingHello, 田中を出力した上で、子のgreetingで 1 行追加したい」という場合に、子のgreetingの中でsuper().greeting()を最初に呼んでから自分の処理を書きます。

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age  = age

    def greeting(self):
        print(f"Hello, {self.name}")


class Employee(Person):
    def greeting(self):
        super().greeting()                            # ① 親の挨拶を先に出す
        print(f"I'm Employee {self.name}")            # ② 子の挨拶を続ける


Employee("田中", 45).greeting()
# Hello, 田中
# I'm Employee 田中
super().greeting() で親の処理を活かす
Employee.greeting()子の greeting本体super().greeting()親 greetingを実行残りの子の処理呼出実行
子のgreetingの中でsuper().greeting()を呼ぶと、親のgreetingそのまま実行される。selfは自動で渡されるので書かなくてよい。

__init__ をオーバーライドして属性を増やす

super()がいちばん活躍するのが__init__のオーバーライドです。子クラスで「親の属性に加えて、子だけが持つ属性を追加したい」というケース — たとえばEmployeeにはphone_numberも持たせたい、というときです。

この場合、__init__ を子クラスでも書き直すことになりますが、親の初期化処理(self.name = name など)は super().__init__(name, age) で 1 行で済ませて、その後に追加分だけ書くのが定石です。これでname / ageの代入を 2 重に書かずに済みます。

子の __init__ で親 + 子の属性をそろえる
Employee("田中", 45, "080-...")子の __init__が走るsuper().__init__(name, age)親が name / ageを代入self.phone_number= phone_number親の属性と子の属性が揃う呼出
Employee(...)を呼ぶと子の__init__が走る → super().__init__(name, age)で親がname / ageを代入 → そのあと子がphone_numberを追加。親の属性 + 子の属性が揃った状態でインスタンスが完成する。
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age  = age


class Employee(Person):
    def __init__(self, name, age, phone_number):
        super().__init__(name, age)               # ① 親の __init__ で name / age をセット
        self.phone_number = phone_number           # ② 子だけの属性を追加

    def show(self):
        print(f"{self.name} / {self.age} / {self.phone_number}")


Employee("田中", 45, "080-1111-2222").show()
# 田中 / 45 / 080-1111-2222

super().__init__(...) を呼び忘れると属性が消える

__init__をオーバーライドしたのにsuper().__init__(...) を呼ばなかった場合、親側で行われていたself.name = nameなどの代入が実行されないままになります。その後でself.nameを読みに行くとAttributeErrorになるので、__init__を子クラスで書き直すときは真っ先に super().__init__(...) を呼ぶようにしましょう。

Employeephone_numberを追加した__init__を書きます。

class Person:を定義し、__init__(self, name, age)self.name / self.ageを代入してください。

class Employee(Person):を定義し、__init__(self, name, age, phone_number)を書いてください。先頭でsuper().__init__(name, age)を呼んでからself.phone_number = phone_numberを代入します。

Employeeの中にshow(self)を定義し、print(f"{self.name} / {self.age} / {self.phone_number}")を実行してください。

Employee("田中", 45, "080-1111-2222")を作ってshow()を呼んでください。

Python エディタ

コードを実行してください
QUIZ

理解度チェック

まずは1問ずつ答えてみましょう。

Q1次のクラス定義のうち、DogAnimalを継承する正しい書き方はどれですか?

Q2メソッドのオーバーライドに関する説明として、最も適切なものはどれですか?

Q3子クラスの__init__super().__init__(...)書き忘れた場合、何が起きますか?