【Python】レッスン5-09:抽象クラスを理解しよう

一つ前のLessonではモジュールについて学習しました。
今回は抽象クラスについて見ていきましょう。
Lesson1:基礎文法編
Lesson2:制御構造編
Lesson3:関数とスコープ編
Lesson4:データ構造編
Lesson5:オブジェクト指向編
・Lesson5-1:クラスの基本を理解しよう
・Lesson5-2:メソッドの基本を理解しよう
・Lesson5-3:カプセル化を理解しよう
・Lesson5-4:プロパティを理解しよう
・Lesson5-5:クラスの継承を理解しよう
・Lesson5-6:メソッドのオーバーライドを理解しよう
・Lesson5-7:静的メソッドを理解しよう
・Lesson5-8:モジュールを使いこなそう
・Lesson5-9:抽象クラスを理解しよう ◁今回はココ
・Lesson5-10:ミックスインを理解しよう
・Lesson5-11:データクラスを理解しよう
・練習問題5-1:モンスター捕獲ゲームを作ろう
・練習問題5-2:モンスターとのバトルゲームを作ろう
次のステップ:Python基礎習得者にお勧めの道5選(実務or副業)
抽象クラスとは?共通機能の定義と実装ルールの設定方法
プログラムを作成する際、コードを再利用しやすく、メンテナンスしやすく設計することが重要です。
抽象クラスはその設計に役立つ概念で、共通の基盤を提供しつつ具象クラスでの独自実装を促す仕組みです。
抽象クラスとは|@abstractmethod
デコレーターの使い方
抽象クラスとは、直接インスタンス化されることを想定しない基底クラス(継承されることを前提としたクラス)です。
主に以下のような目的で使用されます:
- 子クラスで実装する必要がある共通のメソッドを定義
- 設計の一貫性を保証
Pythonでは abc
モジュール(Abstract Base Classesの略)に含まれる ABC
クラス と @abstractmethod
デコレーター を使用します。
以下に基本的な構文を示します:
from abc import ABC, abstractmethod # abcモジュールからABCクラスとabstractmethodデコレータをインポート class クラス名(ABC): # ABCクラスを継承した抽象クラスAnimalを定義 @abstractmethod # 抽象メソッドであることを表すデコレーター def メソッド名(self): # 抽象メソッド(必ずオーバーライドされないといけない)
抽象メソッドは具体的な処理を持たず、子クラスからオーバーライドされる前提となっています。
また、抽象クラスを直接インスタンス化しようとするとエラーが発生します。
抽象クラスの使用コード例
以下は具体的な使用例です。
動物クラスを基底クラスとして作成し、子クラスで動作を具体化します。
from abc import ABC, abstractmethod # abc関連のモジュールのインポート class Animal(ABC): # 抽象クラスの定義 @abstractmethod # デコレーター def speak(self): # 抽象メソッドの定義 pass @abstractmethod # デコレーター def move(self): # 抽象メソッドの定義 pass class Dog(Animal): # Animalクラスを継承したDogクラスの定義 def speak(self): # 抽象メソッドの実装 & オーバーライド return "Woof!" def move(self): # 抽象メソッドの実装 & オーバーライド return "Runs on four legs" class Bird(Animal): # Animalクラスを継承したBirdクラスの定義 def speak(self): # 抽象メソッドの実装 & オーバーライド return "Chirp!" def move(self): # 抽象メソッドの実装 & オーバーライド return "Flies in the sky" dog = Dog() # インスタンス生成 bird = Bird() # インスタンス生成 print(dog.speak()) # Woof! print(dog.move()) # Runs on four legs print(bird.speak()) # Chirp! print(bird.move()) # Flies in the sky # 二つの子クラスは同じ抽象クラスを継承したが、出力はクラスごとに異なる
この例ではAnimal
という抽象クラスを基にしてDog
とBird
というクラスを作成しています。
子クラスでAnimal
クラスのメソッドを実装することで、各動物の特性を定義できます。
Woof! Runs on four legs Chirp! Flies in the sky
まとめ
ここまでで、抽象クラス(ABC)の役割と、@abstractmethod
で実装必須メソッドを宣言する基本手順を確認しました。
抽象クラスは「共通API=契約」を先に定め、未実装のままではインスタンス化できないことで、サブクラス側に実装を促します。
加えて、共通の下地(具象メソッドやテンプレートメソッド)を親に置き、差分だけを子で実装する設計も可能です。
この学習により、API を統一しつつ多態性を安全に活かすクラス設計、プラグイン/戦略パターンのような差し替え可能な構成、そして未実装の取りこぼしを型レベルで防ぐ運用ができるようになりました。
まずは既存の共通処理を「契約」として切り出し、抽象メソッドで “必ず実装すべき点” を明示するところから一歩ずつ進めていきましょう。
- サイト改善アンケート|ご意見をお聞かせください(1分で終わります)
-
本サイトでは、みなさまの学習をよりサポートできるサービスを目指しております。
そのため、みなさまの「プログラミングを学習する理由」などをアンケート形式でお伺いしています。1分だけ、ご協力いただけますと幸いです。
【Python】サイト改善アンケート
練習問題:抽象クラスを使ってみよう
この記事で学習した「抽象クラス」を復習する練習問題に挑戦しましょう。
問題|犬と鳥の動作をクラスで表現しよう
動物を表す抽象クラスを用意し、その契約(鳴き声・移動)の実装を犬と鳥のクラスに任せるミニプログラムを作成します。
最後に、生成したオブジェクトから鳴き声と移動方法を取得して表示してください。
以下の要件に従ってコードを完成させてください。
abc
モジュールを用い、抽象クラスAnimal
を定義する。Animal
はABC
を継承し、make_sound(self)
とmove(self)
を@abstractmethod
で宣言する(抽象メソッド)。Dog
クラスとBird
クラスをAnimal
から継承し、各抽象メソッドを文字列を戻り値として実装する。Dog.make_sound()
は「ワンワン!」、Dog.move()
は「走り回る」。Bird.make_sound()
は「ピヨピヨ!」、Bird.move()
は「空を飛ぶ」。- スクリプト末尾で
Dog
とBird
のインスタンスを生成し、鳴き声と移動方法を表示する。
ただし、以下のような実行結果となるコードを書くこと。
犬の鳴き声: ワンワン! 犬の移動方法: 走り回る 鳥の鳴き声: ピヨピヨ! 鳥の移動方法: 空を飛ぶ
ヒント|難しいと感じる人だけ見よう
1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。
- ヒント1【コードの構成を見る】
-
正解のコードは上から順に以下のような構成となっています。
1:抽象クラスを定義し、「鳴き声」と「移動」の実装契約を宣言する
・抽象クラスは専用モジュールを使い、基底クラスは ABC を継承する
・必ず実装させたいメソッド名は make_sound と move にし、抽象メソッドとして宣言する
・抽象メソッドは本体を持たず、中身は空(実装はサブクラス側で行う)2:犬と鳥のサブクラスで抽象メソッドを具体化し、振る舞いを与える
・抽象メソッドと同じシグネチャ(名前・引数)で実装する
・返すのは文字列(鳴き声/移動方法)で、表示は後段に任せる
・犬と鳥で意味のある違いを持たせ、共通の呼び出しで比較できるようにする3:インスタンスを生成し、共通インターフェースで呼び出して結果を表示する
・まず犬と鳥のインスタンスを1つずつ生成する
・両者に対してmake_sound
とmove
を同じ手順で呼び出す
・返ってきた文字列に説明ラベルを付けて表示すると読みやすい
- ヒント2【穴埋め問題にする】
-
以下のコードをコピーし、コメントに従ってコードを完成させて下さい。
'''(穴埋め) abcモジュールから ABC と abstractmethod をインポートする行を書く ''' # 動物の基底クラス(抽象クラス) class Animal('''(穴埋め)'''): @abstractmethod def make_sound(self): # 動物の鳴き声を出力する抽象メソッド pass @'''(穴埋め)''' def move(self): # 動物がどのように移動するかを定義する抽象メソッド pass # 犬クラス(Animalを継承) class Dog(Animal): def make_sound(self): return "ワンワン!" def move(self): return "走り回る" # 鳥クラス(Animalを継承) class Bird(Animal): def make_sound(self): return "ピヨピヨ!" def move(self): return "空を飛ぶ" # 犬と鳥のインスタンスを生成 dog = Dog() bird = Bird() # 犬の鳴き声と動作 print("犬の鳴き声:", dog.make_sound()) print("犬の移動方法:", dog.move()) # 鳥の鳴き声と動作 print("鳥の鳴き声:", bird.make_sound()) print("鳥の移動方法:", bird.move())
このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。
解答例|動物クラスの抽象インターフェースプログラム
例えば以下のようなプログラムが考えられます。
- 正解コード
-
from abc import ABC, abstractmethod # abcモジュールをインポート # 動物の基底クラス(抽象クラス) class Animal(ABC): @abstractmethod def make_sound(self): # 動物の鳴き声を出力する抽象メソッド pass @abstractmethod def move(self): # 動物がどのように移動するかを定義する抽象メソッド pass # 犬クラス(Animalを継承) class Dog(Animal): def make_sound(self): return "ワンワン!" def move(self): return "走り回る" # 鳥クラス(Animalを継承) class Bird(Animal): def make_sound(self): return "ピヨピヨ!" def move(self): return "空を飛ぶ" # 犬と鳥のインスタンスを生成 dog = Dog() bird = Bird() # 犬の鳴き声と動作 print("犬の鳴き声:", dog.make_sound()) print("犬の移動方法:", dog.move()) # 鳥の鳴き声と動作 print("鳥の鳴き声:", bird.make_sound()) print("鳥の移動方法:", bird.move())
解答例の解説|動物クラスの抽象インターフェースプログラムの考え方
解答例の詳細解説は以下の通りです。
- 詳細解説
-
抽象クラスの定義
from abc import ABC, abstractmethod # abcモジュールをインポート # 動物の基底クラス(抽象クラス) class Animal(ABC): @abstractmethod def make_sound(self): # 動物の鳴き声を出力する抽象メソッド pass @abstractmethod def move(self): # 動物がどのように移動するかを定義する抽象メソッド pass
ここでは、動物という概念を直接は作れない“型の設計図”として用意します。
抽象クラスを使うと、派生クラスが必ず備えるべきメソッドを先に約束として示せます。
具体的には、鳴き声と移動の2つを「ここは各動物で必ず実装してください」という契約にしておきます。
こうしておくと、犬や鳥のようなサブクラスを作るときに、必要なメソッドを漏れなく実装でき、共通の呼び出し方で扱えるようになります。
抽象メソッドは中身を持たないため、この時点ではインスタンス化はできません(未実装のまま使おうとするとエラーになります)。子クラスの定義
# 犬クラス(Animalを継承) class Dog(Animal): def make_sound(self): return "ワンワン!" def move(self): return "走り回る" # 鳥クラス(Animalを継承) class Bird(Animal): def make_sound(self): return "ピヨピヨ!" def move(self): return "空を飛ぶ"
ここでは、抽象クラスで約束したメソッドに中身を与えて、実際に動く振る舞いへ落とし込みます。
犬と鳥は同じ名前のメソッドを持ちますが、返す内容はそれぞれの動物にふさわしいテキストになります。こうしておくと、呼び出し側は型ごとの差を意識せず同じ操作で扱えます。
つまり「同じインターフェース、違う結果」という多態性を、短い例で体験できるわけです。
ここでは表示は行わず、文字列を返すだけにしておくことで、出力の方法や場所は後段の処理に委ねられます。インスタンス生成と出力
# 犬と鳥のインスタンスを生成 dog = Dog() bird = Bird() # 犬の鳴き声と動作 print("犬の鳴き声:", dog.make_sound()) print("犬の移動方法:", dog.move()) # 鳥の鳴き声と動作 print("鳥の鳴き声:", bird.make_sound()) print("鳥の移動方法:", bird.move())
ここでは、犬と鳥のオブジェクトを実際に作り、抽象クラスで約束した同じメソッド名を使って振る舞いを取り出し、画面に表示します。
どちらのオブジェクトも同じ呼び出し方で扱えるのは、基底に共通の契約があるからです。
返ってくるのは文字列なので、表示用のラベル(「犬の鳴き声: 」「鳥の移動方法: 」など)を添えて出力すれば、結果が分かりやすくなります。
型ごとの違いは内部の実装に任せ、呼び出し側は同じ手順で処理できる——これが多態性のメリットです。
抽象クラスの疑問解消|FAQと用語のまとめ
初心者がつまずきやすいポイントをFAQとしてまとめ、またよく使う専門用語をわかりやすく整理しました。
理解を深めたいときや、ふと疑問に感じたときに役立ててください。
FAQ|抽象クラスに関するよくある質問
- Q1. 抽象クラスはどのようなときに使いますか?
-
子クラスに共通のインターフェースを強制したいときや、基盤クラスの設計を厳密にしたいときに使います。
- Q2. 抽象クラスとインターフェースの違いは?
-
Pythonには明確なインターフェース機能はありませんが、抽象クラスでインターフェースのような動作を実現できます。
- Q3. 抽象クラスはインスタンス化できますか?
-
いいえ、抽象クラスは直接インスタンス化できず、継承したサブクラスでメソッドを実装する必要があります。
Python用語集|抽象クラスに関する用語一覧
今回の記事で出てきた用語・関数などを一覧で紹介します。
このサイトに出てくる 全てのPython用語をまとめた用語集 も活用してください。
Python用語 | 定義・使い方の概要 | 解説記事へのリンク |
---|---|---|
抽象クラス | 直接インスタンス化できないクラスで、派生クラスに共通のインターフェースを定義するために使う | 本記事 |
抽象プロパティ | サブクラスで実装を強制するプロパティ。abc モジュールと @property を組み合わせて定義される | 本記事 |
抽象メソッド | サブクラスでのオーバーライドを必須とするメソッド。基底クラスで @abstractmethod を使って宣言される | 本記事 |
abc モジュール | 抽象基底クラスを定義するための標準モジュール。抽象クラスや抽象メソッドの宣言に使用される | 本記事 |
継承 | あるクラス(親クラス)の機能を別のクラス(子クラス)が引き継ぐこと。コードの再利用を促進する | Lesson5-5 |
オーバーライド | 親クラスで定義されたメソッドを子クラスで再定義し、振る舞いを変更すること | Lesson5-6 |