【Python入門】クラスの継承を初心者向けに完全解説!基礎から応用まで

 

Pythonのクラス継承は、オブジェクト指向プログラミングの核となる重要な概念です。継承を使うことで、既存のクラスの機能を引き継ぎながら、新しい機能を追加したり、既存の機能を変更したりできます。この記事では、クラス継承の基本から実践的な使い方まで、初心者の方にも分かりやすく解説します。

継承とは?

継承とは、既存のクラス(親クラス・基底クラス)の属性やメソッドを新しいクラス(子クラス・派生クラス)が受け継ぐ仕組みです。現実世界で言えば、「動物」という親から「犬」や「猫」という子が特徴を受け継ぐようなイメージです。

継承のメリット

  • コードの再利用: 既存のコードを再度書く必要がない
  • 保守性の向上: 共通部分の修正が一箇所で済む
  • 拡張性: 既存機能を保ちながら新機能を追加
  • 階層構造: 関連するクラス同士の関係が明確

基本的な継承の書き方

1. 最もシンプルな継承

class Animal:  # 親クラス(基底クラス)
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        print(f"{self.name}が鳴いています")

class Dog(Animal):  # 子クラス(派生クラス)
    pass

dog = Dog("ポチ")
dog.speak()  # ポチが鳴いています

2. 子クラスでメソッドを追加

class Animal:
    def __init__(self, name):
        self.name = name
    
    def eat(self):
        print(f"{self.name}が食事をしています")

class Dog(Animal):
    def bark(self):  # 新しいメソッドを追加
        print(f"{self.name}がワンワンと吠えています")

dog = Dog("ポチ")
dog.eat()   # ポチが食事をしています
dog.bark()  # ポチがワンワンと吠えています

3. 子クラスでメソッドをオーバーライド

class Animal:
    def __init__(self, name):
        self.name = name
    
    def speak(self):
        print(f"{self.name}が鳴いています")

class Cat(Animal):
    def speak(self):  # メソッドをオーバーライド
        print(f"{self.name}がニャーと鳴いています")

cat = Cat("タマ")
cat.speak()  # タマがニャーと鳴いています

継承の基本構文

class 親クラス名:
    def __init__(self, 引数):
        self.属性 = 引数
    
    def メソッド名(self):
        処理内容

class 子クラス名(親クラス名):  # 継承の指定
    def __init__(self, 引数):
        super().__init__(引数)  # 親クラスの初期化を呼び出し
    
    def 新しいメソッド(self):
        処理内容

super()を使った親クラスの呼び出し

1. __init__メソッドでのsuper()

class Vehicle:
    def __init__(self, brand, model):
        self.brand = brand
        self.model = model
    
    def info(self):
        print(f"{self.brand} {self.model}")

class Car(Vehicle):
    def __init__(self, brand, model, doors):
        super().__init__(brand, model)  # 親クラスの初期化
        self.doors = doors  # 子クラス独自の属性
    
    def info(self):
        super().info()  # 親クラスのメソッド呼び出し
        print(f"ドア数: {self.doors}")

car = Car("トヨタ", "プリウス", 4)
car.info()
# トヨタ プリウス
# ドア数: 4

2. メソッドの拡張

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self.salary = salary
    
    def work(self):
        print(f"{self.name}が働いています")

class Manager(Employee):
    def __init__(self, name, salary, team_size):
        super().__init__(name, salary)
        self.team_size = team_size
    
    def work(self):
        super().work()  # 親クラスの処理を実行
        print(f"チーム管理もしています({self.team_size}人)")

manager = Manager("田中", 500000, 5)
manager.work()
# 田中が働いています
# チーム管理もしています(5人)

実践的な継承の例

1. 図形クラスの階層

class Shape:
    def __init__(self, color):
        self.color = color
    
    def area(self):
        return 0
    
    def info(self):
        print(f"色: {self.color}, 面積: {self.area()}")

class Rectangle(Shape):
    def __init__(self, color, width, height):
        super().__init__(color)
        self.width = width
        self.height = height
    
    def area(self):
        return self.width * self.height

class Circle(Shape):
    def __init__(self, color, radius):
        super().__init__(color)
        self.radius = radius
    
    def area(self):
        return 3.14 * self.radius ** 2

rect = Rectangle("赤", 10, 5)
circle = Circle("青", 3)

rect.info()    # 色: 赤, 面積: 50
circle.info()  # 色: 青, 面積: 28.26

2. ゲームキャラクターの階層

class Character:
    def __init__(self, name, hp, attack):
        self.name = name
        self.hp = hp
        self.max_hp = hp
        self.attack = attack
    
    def take_damage(self, damage):
        self.hp -= damage
        if self.hp < 0:
            self.hp = 0
    
    def attack_enemy(self, enemy):
        print(f"{self.name}が{enemy.name}を攻撃!")
        enemy.take_damage(self.attack)

class Warrior(Character):
    def __init__(self, name, hp, attack, defense):
        super().__init__(name, hp, attack)
        self.defense = defense
    
    def special_attack(self, enemy):
        damage = self.attack * 2
        print(f"{self.name}の必殺技!")
        enemy.take_damage(damage)

class Mage(Character):
    def __init__(self, name, hp, attack, mana):
        super().__init__(name, hp, attack)
        self.mana = mana
    
    def cast_spell(self, enemy):
        if self.mana >= 10:
            damage = self.attack * 1.5
            self.mana -= 10
            print(f"{self.name}が魔法を唱えた!")
            enemy.take_damage(damage)

warrior = Warrior("戦士", 100, 20, 15)
mage = Mage("魔法使い", 80, 25, 50)

warrior.attack_enemy(mage)  # 戦士が魔法使いを攻撃!
mage.cast_spell(warrior)    # 魔法使いが魔法を唱えた!

3. 銀行口座の階層

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance
    
    def deposit(self, amount):
        self.balance += amount
        print(f"{amount}円入金しました")
    
    def withdraw(self, amount):
        if self.balance >= amount:
            self.balance -= amount
            print(f"{amount}円出金しました")
            return True
        return False

class SavingsAccount(BankAccount):
    def __init__(self, owner, balance=0, interest_rate=0.01):
        super().__init__(owner, balance)
        self.interest_rate = interest_rate
    
    def add_interest(self):
        interest = self.balance * self.interest_rate
        self.balance += interest
        print(f"利息{interest}円が追加されました")

class CheckingAccount(BankAccount):
    def __init__(self, owner, balance=0, overdraft_limit=10000):
        super().__init__(owner, balance)
        self.overdraft_limit = overdraft_limit
    
    def withdraw(self, amount):
        if self.balance + self.overdraft_limit >= amount:
            self.balance -= amount
            print(f"{amount}円出金しました")
            return True
        print("引き出し限度額を超えています")
        return False

savings = SavingsAccount("田中", 100000)
checking = CheckingAccount("佐藤", 50000)

savings.add_interest()    # 利息1000.0円が追加されました
checking.withdraw(55000)  # 55000円出金しました

多重継承

基本的な多重継承

class Flyable:
    def fly(self):
        print("飛んでいます")

class Swimmable:
    def swim(self):
        print("泳いでいます")

class Duck(Flyable, Swimmable):
    def __init__(self, name):
        self.name = name
    
    def quack(self):
        print(f"{self.name}がガーガー鳴いています")

duck = Duck("アヒル")
duck.fly()    # 飛んでいます
duck.swim()   # 泳いでいます
duck.quack()  # アヒルがガーガー鳴いています

MRO(Method Resolution Order)

class A:
    def method(self):
        print("Aのメソッド")

class B(A):
    def method(self):
        print("Bのメソッド")

class C(A):
    def method(self):
        print("Cのメソッド")

class D(B, C):
    pass

d = D()
d.method()  # Bのメソッド
print(D.__mro__)  # メソッド解決順序を確認

抽象クラスとポリモーフィズム

抽象クラスの実装

from abc import ABC, abstractmethod

class Animal(ABC):
    def __init__(self, name):
        self.name = name
    
    @abstractmethod
    def speak(self):
        pass  # 抽象メソッド(子クラスで必ず実装)
    
    def sleep(self):
        print(f"{self.name}が眠っています")

class Dog(Animal):
    def speak(self):  # 必須の実装
        print(f"{self.name}がワンワン")

class Cat(Animal):
    def speak(self):  # 必須の実装
        print(f"{self.name}がニャー")

# animal = Animal("動物")  # エラー: 抽象クラスはインスタンス化不可
dog = Dog("ポチ")
cat = Cat("タマ")

dog.speak()  # ポチがワンワン
cat.speak()  # タマがニャー

isinstance()とissubclass()

型チェックの方法

class Animal:
    pass

class Dog(Animal):
    pass

class Cat(Animal):
    pass

dog = Dog()
cat = Cat()

# isinstance(): インスタンスの型チェック
print(isinstance(dog, Dog))     # True
print(isinstance(dog, Animal))  # True(継承関係)
print(isinstance(dog, Cat))     # False

# issubclass(): クラス同士の継承関係チェック
print(issubclass(Dog, Animal))  # True
print(issubclass(Cat, Animal))  # True
print(issubclass(Dog, Cat))     # False

よくある間違いと注意点

1. super()の呼び忘れ

# ❌ 間違い
class Parent:
    def __init__(self, value):
        self.value = value

class Child(Parent):
    def __init__(self, value, extra):
        # super().__init__(value)  # 忘れている
        self.extra = extra

# ✅ 正解
class Child(Parent):
    def __init__(self, value, extra):
        super().__init__(value)  # 親クラスの初期化
        self.extra = extra

2. 循環継承

# ❌ 間違い(循環継承)
# class A(B):
#     pass
# 
# class B(A):  # エラー: 循環継承
#     pass

# ✅ 正解
class A:
    pass

class B(A):
    pass

3. 深い継承階層

# ❌ 避けるべき(継承が深すぎる)
class A:
    pass
class B(A):
    pass
class C(B):
    pass
class D(C):
    pass
class E(D):  # 継承が深すぎて複雑
    pass

# ✅ 推奨(適切な深さ)
class Vehicle:
    pass
class Car(Vehicle):
    pass
class SportsCar(Car):  # 2-3階層程度に抑える
    pass

継承のベストプラクティス

1. is-a関係を意識する

# ✅ 適切な継承(is-a関係)
class Animal:
    pass

class Dog(Animal):  # 犬は動物である
    pass

# ❌ 不適切な継承
class House:
    pass

class Dog(House):  # 犬は家ではない
    pass

2. コンポジションも検討する

# 継承よりコンポジションが適切な場合
class Engine:
    def start(self):
        print("エンジン始動")

class Car:
    def __init__(self):
        self.engine = Engine()  # has-a関係(コンポジション)
    
    def start(self):
        self.engine.start()

まとめ

Pythonのクラス継承は、効率的で保守性の高いプログラムを作成するための強力な機能です。適切に使用することで、コードの再利用性と拡張性を大幅に向上させることができます。

重要なポイント

  • 継承はis-a関係を表現する
  • super()で親クラスの機能を呼び出す
  • メソッドオーバーライドで機能をカスタマイズ
  • 抽象クラスで共通インターフェースを定義
  • 継承よりコンポジションが適切な場合もある

まずは簡単な継承から始めて、徐々に複雑な階層構造を設計できるようになりましょう。実際にコードを書いて練習することで、継承の概念がしっかりと身に付きます!

■プロンプトだけでオリジナルアプリを開発・公開してみた!!

■AI時代の第一歩!「AI駆動開発コース」はじめました!

テックジム東京本校で先行開始。

■テックジム東京本校

「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。

<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。

<月1開催>放送作家による映像ディレクター養成講座

<オンライン無料>ゼロから始めるPython爆速講座