Python環境を起動中... 初回のみ数秒かかります
Lesson 14 / 20

継承

このレッスンで学ぶこと

  • 親クラスを継承した子クラスを作れる
  • super() で親クラスの __init__ を呼べる
  • 親クラスのメソッドをオーバーライド(上書き)できる
  • isinstance() でクラスの種類を判定できる

1. 継承の基本

class 子クラス(親クラス): と書くと、親クラスの属性・メソッドをすべて引き継ぎます。 共通部分を親クラスに書き、差分だけ子クラスで追加・上書きするのが継承の考え方です。

継承を使う主なメリットは コードの重複を減らせること です。 医師・看護師・技師など職種が増えても、共通の「氏名・年齢・部署」の管理ロジックは 親クラスに1つ書けば済みます。後から仕様が変わっても親クラスを直すだけで全職種に反映されます。

sample_1.py
Ctrl+Enter
出力

💡 super() とは? 親クラスを参照するための関数です。super().__init__(...) で親クラスの初期化処理を実行します。これを忘れると親の属性(self.name など)が設定されず、親のメソッドを使おうとしたときに AttributeError になります。

📋 継承ツリーのイメージ:

MedicalStaff(親)
├── Doctor(子)  … introduce() を継承 + diagnose() を追加
└── Nurse(子)   … introduce() を継承 + administer() を追加

共通の introduce() は親クラスに1つ書けば、すべての子クラスで使えます。

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

子クラスで親クラスと同じ名前のメソッドを定義すると、上書き(オーバーライド)されます。 super().メソッド名() で親の処理を呼び出した上で追加処理を加えることもできます。

オーバーライドは「親の動作では不十分なとき」に使います。 たとえば Doctor の業務内容は MedicalStaff の標準業務とは異なるため、 work() を専用の内容で上書きします。

sample_2.py
Ctrl+Enter
出力

💡 super() の使い分け:

🔑 ポリモーフィズム(多態性)とは、同じメソッド名で呼び出しても、オブジェクトの種類によって異なる動作をすることです。staff.work()doctor.work()chief.work() はすべて work() という名前でも、それぞれ違う処理が実行されます。「インターフェースを統一しつつ実装を分ける」というオブジェクト指向設計の核心です。

3. isinstance() と issubclass()

isinstance(obj, クラス) でオブジェクトが特定クラス(またはその子クラス)のインスタンスかを確認できます。 型の判定や分岐処理でよく使います。

issubclass(子クラス, 親クラス) はオブジェクトではなくクラス同士の関係を調べます。 「このクラスはあのクラスを継承しているか?」を確認するときに使います。

sample_3.py
Ctrl+Enter
出力

💡 isinstance() vs type() の違い:

  • isinstance(doc, MedicalStaff)True(継承関係を含めて判定する)
  • type(doc) == MedicalStaffFalse(厳密に同じ型のみ判定する)

DoctorMedicalStaff の一種である」という継承の意味を正しく扱えるため、通常は isinstance() を使います。

4. 継承でニューラルネット層を表現する

📌 AI/PyTorch の文脈に触れる内容です・高難易度のためスキップ可: 今はピンとこなくても大丈夫です。PyTorch や AI資格の学習を始めたときに「あ、継承ってこういう場面で使うんだ」と戻ってくるつもりで眺めるだけでも構いません。

PyTorch の各層(nn.Linearnn.Conv2d)はすべて nn.Module という基底クラスを継承しています。 このレッスンで学んだ継承と全く同じパターンで作られています。ここでは純粋な Python でその仕組みを再現してみましょう。

🔑 このコードに登場する新しい構文:

  • raise NotImplementedError("...") — 「このメソッドはサブクラスで必ず実装してください」という意味のエラーを意図的に発生させます。実装し忘れた場合にすぐ気づけるよう、抽象メソッドの代替として使う定番パターンです。
  • def __call__(self, x): — インスタンスを net(入力) のように関数として呼び出したときに自動で実行される特殊メソッド(L15 で詳しく扱います)。PyTorch では model(x) と書くと内部でこれが動き、forward() を呼び出します。
  • getattr(obj, "属性名")obj.属性名 と同じ意味ですが、属性名を文字列として渡せます。ループ内で属性名を動的に変えたいときに使います(例:for name in ["conv1", "fc1"])。
sample_4.py
Ctrl+Enter
出力

📊 順伝播(forward pass)の呼び出しの流れ:

  1. net("画像入力") と書いて呼び出す
  2. Pythonが自動的に net.forward(x) を実行する
  3. forward の中で conv1 → fc1 → fc2 の順に処理が流れる
  4. 最後の fc2 の結果が output として返ってくる

net("入力") と1行書くだけで、定義した層が順番に自動で呼ばれます。これは実際の PyTorch モデルの動き方と全く同じ構造です。

5. 練習問題

問題 1

PersonからDoctorを継承する

Person クラス(nameagegreet())を親として、Doctor クラスを作成してください。Doctor は追加属性 specialty(専門)を持ち、greet() をオーバーライドして「○○医師(□□専門)です」と表示します。

exercise_1.py
出力
ヒントを見る(答え+解説)
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def greet(self):
        print(f"こんにちは、{self.name}({self.age}歳)です")

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

    def greet(self):
        print(f"{self.name}医師({self.specialty}専門)です")

doc = Doctor("佐藤一郎", 42, "循環器科")
doc.greet()
# → 佐藤一郎医師(循環器科専門)です

super().__init__(name, age) で親クラスの初期化処理を呼び出し、子クラス独自の属性を追加します。greet() をオーバーライドすることで親とは違う動作に差し替えられます。

問題 2

職種ごとに分岐処理

DoctorNurse のリストから、isinstance() を使って職種ごとに処理を分けてください。Doctorは「診察可能」、Nurseは「処置可能」と表示します。

exercise_2.py
出力
ヒントを見る(答え+解説)
class MedicalStaff:
    def __init__(self, name):
        self.name = name

class Doctor(MedicalStaff):
    pass

class Nurse(MedicalStaff):
    pass

staff_list = [
    Doctor("佐藤医師"),
    Nurse("田中看護師"),
    Doctor("山田医師"),
    Nurse("鈴木看護師"),
]

for person in staff_list:
    if isinstance(person, Doctor):
        print(f"{person.name}: 診察可能")
    elif isinstance(person, Nurse):
        print(f"{person.name}: 処置可能")
# → 佐藤医師: 診察可能 / 田中看護師: 処置可能 / ...

isinstance(オブジェクト, クラス名) はそのオブジェクトが指定クラスのインスタンスかどうかを判定します。継承関係があっても正しく判定でき、医療システムでの職種別処理によく使います。

問題 3

super() で親の処理を流用

Patient クラスに report() メソッドがあります。ICUPatient クラスで report() をオーバーライドし、super().report() を呼んだ後に「ICU管理中: □□」と追記してください。

exercise_3.py
出力
ヒントを見る(答え+解説)
class Patient:
    def __init__(self, name, diagnosis):
        self.name = name
        self.diagnosis = diagnosis

    def report(self):
        print(f"患者: {self.name}, 診断: {self.diagnosis}")

class ICUPatient(Patient):
    def __init__(self, name, diagnosis, device):
        super().__init__(name, diagnosis)
        self.device = device

    def report(self):
        super().report()
        print(f"ICU管理中: {self.device}")

icu = ICUPatient("田中太郎", "重症肺炎", "人工呼吸器")
icu.report()
# → 患者: 田中太郎, 診断: 重症肺炎
# → ICU管理中: 人工呼吸器

super().report() で親クラスの処理をそのまま実行した後、子クラス独自の追加情報を出力します。コードを重複させずに機能を拡張できる継承の典型的な使い方です。

まとめ

このレッスンのポイント

  • class 子(親): で親クラスの属性・メソッドをすべて引き継ぐ
  • super().__init__(...) で親の初期化を実行する(忘れずに!)
  • 子クラスで同名のメソッドを定義するとオーバーライドされる
  • super().メソッド() で親の処理を呼び出しながら追加処理できる
  • isinstance(obj, クラス) は親クラスに対しても True を返す
  • PyTorchの nn.Linear, nn.Conv2d はすべて nn.Module を継承している

自由に試してみましょう:

free_practice.py
Ctrl+Enter
出力

完了するとトップページに進捗が表示されます