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
を起こし、設計ミスに強い。
お読み頂きありがとうございました。