【Python】抽象クラス・基底クラス・抽象基底クラスの違いをわかりやすく解説!

Pythonでオブジェクト指向プログラミングをしていると、「基底クラス(Base Class)」「抽象クラス(Abstract Class)」「抽象基底クラス(Abstract Base Class / ABC)」という言葉に出会うことがあります。

これらは、クラスの設計や継承を活用するうえで非常に重要な概念ですが、用語が似ていて混同しがちです。

この記事では、それぞれの違いや用途などをサンプルコードを用いてわかりやすく解説します。

抽象クラス・基底クラス・抽象基底クラスの違い

基底クラス」「抽象クラス」「抽象基底クラス」の違いをざっくり表でまとめます。

基底クラス抽象クラス抽象基底クラス
説明通常の親クラス処理のひな型だけを定義するクラスabcモジュールを使った正式な抽象クラス
役割共通処理の継承実装のひな型インターフェースの明示と強制
インスタンス化✅ 可能✅ 可能(ただし実用上は推奨されない)❌ 不可(インスタンス化するとエラー)
メソッド実装の強制❌ 強制なし⚠️ raise NotImplementedErrorで意図的に示す@abstractmethodで強制
適している用途共通機能の継承実装のひな型の提供インターフェース定義、設計ミス防止、堅牢な設計

基底クラスとは?

基底クラスは、複数のクラスに共通する処理をまとめる親クラスです。たとえば、全ての動物に共通する「呼吸する」という機能をAnimalクラスに定義し、犬や猫などの派生クラスに継承させることで、コードの重複を防ぐことができます。

# 基底クラス / 親クラス / スーパークラス
class Animal:
    def breathe(self):
        print("呼吸している")

# 派生クラス / 子クラス / サブクラス(Animalを継承)
class Dog(Animal):
    def bark(self):
        print("ワン!")

# 使用例
dog = Dog()
dog.breathe()  # 呼吸している
dog.bark()     # ワン!

基底クラスの特徴

  • 通常のクラスとして定義する
    • class クラス名:のように特別なモジュールを使わず定義することができる。
  • 自由にインスタンス化できる
    • Animal()など直接インスタンスを作成できる。
  • 他のクラスが継承することで機能を再利用することができる
    • 共通処理をまとめるので、子クラスで何度も同じ処理を書く必要がない。
  • メソッドをオーバーライドして再定義することができる
    • 子クラス側で親クラスのメソッドを上書きして、独自の振る舞いを定義できる。

抽象クラスとは?

抽象クラスは、共通のメソッド構造(インターフェース)を定義するけれど、自分では中身の実装を持たず、子クラスに具体的な処理を任せる「設計のひな型」として使うクラスです。Pythonでは NotImplementedError を使って「ここは子クラスで実装してね」という意図を示します。

# 抽象クラス / 親クラス / スーパークラス
class Shape:
    def area(self):
        raise NotImplementedError("area()メソッドを実装してください")

# 派生クラス / 子クラス / サブクラス(Shapeを継承し、areaを実装)
class Square(Shape):
    def area(self):
        return 5 * 5

# 使用例
s = Square()
print(s.area())  # 25

抽象クラスの特徴

  • インスタンス化は可能だが、実用的には子クラスに実装させる前提である
    • 直接Shape()としてインスタンスを作成できるが、実用的ではない。
  • NotImplementedErrorを使うことで意図を明示
    • メソッドに処理を書かず、例外を投げて子クラスでの実装を促す。
  • 実装ミス(メソッドが未実装)でもインスタンス化できてしまう
    • 設計ミスを見逃すリスクがあるので注意。

raise NotImplementedError を書いた場合と書かなかった場合

raise NotImplementedErrorを書いた場合

class Shape:
    def area(self):
        raise NotImplementedError("area() を子クラスで実装してください")

class Circle(Shape):
    pass

c = Circle()
c.area()  # ここでエラー発生!!エラー内容 → NotImplementedError: area() を子クラスで実装してください

呼び出し時にエラーが起きるので、未実装に気づきやすい。

raise NotImplementedErrorを書かない場合

class Shape:
    def area(self):  # 何もしない
        pass

class Circle(Shape):
    pass

c = Circle()
print(c.area())  # 何もエラーが出ず、None が返る

エラーが発生せず、Noneが返るため、設計ミスに気づきにくい。

抽象基底クラス(ABC)とは?

抽象基底クラスは、Python標準ライブラリのabcモジュールを使って作成する正式な抽象クラスです。@abstractmethodデコレーターを付けることで、子クラスに実装を強制でき、未実装の場合はインスタンス化時にTypeErrorを発生させます。JavaやC#などの「インターフェース」に近い役割を果たします。

from abc import ABC, abstractmethod

# 抽象基底クラス(正式な抽象クラス) / 親クラス / スーパークラス
class Shape(ABC):
    @abstractmethod
    def area(self):
        pass

# 派生クラス / 子クラス / サブクラス(Shapeを継承し、areaを実装)
class Triangle(Shape):
    def area(self):
        return 0.5 * 10 * 5

# 使用例
t = Triangle()
print(t.area())  # 25.0

s = Shape()  # ここでエラー発生!!エラー内容 → TypeError: Can't instantiate abstract class Shape without an implementation for abstract method 'area'

abcモジュールはPython標準ライブラリで、正式な抽象クラスや抽象メソッドを定義するためのものです。

抽象基底クラスの特徴

  • ABCを継承して「抽象クラス」であることを明示する
    • コード上ですぐに抽象クラスだとわかる。
  • @abstractmethodで子クラスに実装を強制できる
    • 実装を忘れるとインスタンス化できずエラーになる。

子クラスで@abstractmethodを実装し忘れた場合

もし子クラスで@abstractmethodのメソッドを実装し忘れた場合は、インスタンス化時に次のようなTypeErrorが発生します。

# 派生クラス / 子クラス / サブクラス(Shapeを継承し、areaを実装していない!!s)
class Rectangle(Shape):
    pass

r = Rectangle()  # ここでエラー発生!!エラー内容 →  TypeError: Can't instantiate abstract class Shape without an implementation for abstract method 'area'

このエラーはインスタンス化時に発生するため、開発中の早い段階で実装漏れに気づけます。これが、抽象クラスとの大きな違いです。抽象クラスの場合、raise NotImplementedErrorを書き忘れてもインスタンス化できてしまいます。メソッドを呼び出したときにNotImplementedErrorが発生します。

本記事のまとめ

この記事では「基底クラス(Base Class)」「抽象クラス(Abstract Class)」「抽象基底クラス(Abstract Base Class / ABC)」について、以下の内容を説明しました。

  • 基底クラス
    • 通常の親クラス。共通処理をまとめて継承させる。
  • 抽象クラス
    • raise NotImplementedErrorを使って「ここは子クラスで実装してください」と示すひな型クラス。インスタンス化可能だが、未実装だとメソッド呼び出し時にエラーになる。
  • 抽象基底クラス(ABC)
    • abcモジュールと@abstractmethodデコレーターを使って定義される正式な抽象クラス。未実装のままだとインスタンス化時にTypeErrorを起こし、設計ミスに強い。

お読み頂きありがとうございました。