【Python】抽象クラスとは?基礎から実務での活用までをやさしく解説

はじめに

皆さんは、同じような機能をもつクラスを複数作るときに「共通の設計をわかりやすくまとめたい」と思ったことはありませんか。

Python には、クラスの基本形を定義しておく抽象クラスという仕組みがあります。

この仕組みを使うと、クラス間の共通点をまとめておき、継承先がそれをきちんと実装するように促すことができます。

たとえば「動物を表すクラス」を何種類も作りたいときに、基本的なメソッド構造をしっかりと決めておける、というイメージです。

そんな抽象クラスの仕組みを、初心者の皆さんにもわかりやすく解説していきます。

ここではできるだけ難解な言葉を避けながら、Pythonにおける抽象クラスの特徴や実際の書き方などを見ていきましょう。

この記事を読むとわかること

  • 抽象クラスの概要
  • Pythonで抽象クラスを使うための基本的な書き方
  • 実務での活用場面と設計上のメリット
  • 継承先のクラスでどのように具体的な処理を書けばよいのか

それでは順番にみていきましょう。

抽象クラスとは?

抽象クラスとは、メソッドの「枠組み」だけを定義して、実際の処理内容は派生クラス(継承先のクラス)で実装するように作られたクラスのことです。

ざっくり言うと、「最低限これだけは実装してほしいメソッド名や引数の形式」 などをまとめておき、ほかのクラスが継承するときの指針を明確にするために存在します。

たとえば、動物に例えると、動物を抽象クラスとして定義し、その中で**「鳴き声を出すメソッドは必ず作りましょう」** としておきます。

実際の猫や犬などの具体的なクラスでは、それぞれ違う鳴き声を設定します。

こうすることで「猫クラスでも犬クラスでも鳴き声を出すメソッドは必ずあるよね」という大事な約束ができるわけです。

ここで「抽象」とは「具体的な実装がない」という意味合いです。

つまり抽象クラス自体を単独で利用することはあまりなく、継承先のクラスがちゃんと実装しなければ使えない、という点が特徴と言えます。

Pythonにおける抽象クラスの基礎

Python では、abc (Abstract Base Class) というモジュールを利用することで抽象クラスを定義できます。

abc は標準ライブラリの一部であり、特別な外部インストールなどは不要です。

ここでは Pythonの中に用意されている ABCMetaabstractmethod を使います。

abcモジュールとは

abc は、クラスを「抽象クラス」として扱うための機能をまとめたモジュールです。

抽象クラスのメタクラスとなる ABCMeta や、メソッドを抽象メソッドに指定するための @abstractmethod デコレータが含まれています。

これらを上手に組み合わせることで、「抽象クラスの定義」や「継承先に未実装のメソッドがあるかどうか」をチェックしてくれます。

抽象クラスを定義するメリット

抽象クラスを導入すると、複数クラス間で共有したいメソッドの名前や引数構成をしっかり固定できるようになります。

具体的には以下のようなメリットがあります。

  • 継承先クラスで必須メソッドの実装忘れが起きにくい
  • 一貫性のあるコード設計になり、可読性が高まりやすい
  • 将来クラスが増えたときでも、共通メソッド名がわかっていればコードを追加しやすい

少しイメージしづらいかもしれませんが、抽象クラスを作っておくと、あとからクラスを増やしたい場面になったときに「何を実装すべきか」がはっきりするのです。

そのため、後々大規模化したり、複数人で同じプロジェクトを進めたりする際にも役立ちます。

抽象クラスの書き方とコード例

シンプルな例

ここでは動物の例を使って、抽象クラスをどんなふうに作るのか見ていきましょう。

import abc

class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def make_sound(self):
        pass
  • class Animal(metaclass=abc.ABCMeta):
    • ここで ABCMeta を指定することで、このクラスが抽象クラスとして扱われます。
  • @abc.abstractmethod
    • メソッドの上にこのデコレータをつけることで「これは抽象メソッドだよ」と宣言します。
    • 抽象メソッドは「中身が定義されていないメソッド」のようなものです。

この状態では、Animal クラスを直接インスタンス化しようとするとエラーが発生します。

なぜなら、抽象メソッド(make_sound)が実装されていないままだからです。

そこで、実際に使うためには下記のように継承先クラスを作り、make_sound の処理を埋める必要があります。

class Cat(Animal):
    def make_sound(self):
        print("ニャー")

class Dog(Animal):
    def make_sound(self):
        print("ワンワン")

これで CatDog をインスタンス化すれば、make_sound を呼び出したときにそれぞれの鳴き声が出力されます。

cat = Cat()
cat.make_sound()  # ニャー

dog = Dog()
dog.make_sound()  # ワンワン

ここでのポイントは、継承先クラスに @abc.abstractmethod を付けたメソッドを実装していないとエラーを起こすという点です。

それゆえに、継承先クラスの設計がしっかりそろいます。

他のメソッドも自由に書ける

なお、抽象クラスの中に抽象メソッド以外の普通のメソッドを定義することも可能です。

「共通の動きを持つメソッドを作っておきたい」という場合に便利です。

import abc

class Animal(metaclass=abc.ABCMeta):
    def walk(self):
        print("とことこ歩き出す")

    @abc.abstractmethod
    def make_sound(self):
        pass

このようにすれば、walk() は継承先クラスでも共通の処理を引き継ぐことが可能です。

継承先のクラスは walk() をそのまま利用できますし、必要に応じてオーバーライド(再定義)することもできます。

実務での活用シーン

抽象クラスは実務でもよく使われます。

特に「同じような性質のクラスを多数定義したいが、最小限共通のメソッドはすべて実装しておきたい」というときに便利です。

複数のファイル形式への共通インターフェース

たとえば、さまざまなファイル形式を扱う処理を考えます。

  • PDFファイルを読み込んで何か解析するクラス
  • Excelファイルを読み込むクラス
  • CSVファイルを読み込むクラス

これらは、それぞれフォーマットが違うだけで「ファイルを開いて中身を処理する」という行為は共通しています。

そこで抽象クラスに「open_file」や「parse_data」などのメソッドを用意し、継承先で実際の実装を行うようにしておくわけです。

すると、利用する側は「ファイルを開いて」「データを処理して」などのメソッド呼び出しがどの形式に対しても同じインターフェースになります。

GUI部品の統一的な設計

GUIを作るとき、ボタンやテキストフィールドなど、多彩な要素をクラスで表現するケースがあります。

それらのオブジェクトが持つ「表示する」「イベントを処理する」「デザインを再描画する」といった動きは共通なので、抽象クラスとして定義しておけば継承先はそれぞれの具体的なボタンやテキストフィールドのクラスを実装するだけで済みます。

結果としてコードが整理され、変更にも強くなります。

複数のAPIクライアントに対応

もし社内で複数のAPIを一元的に扱いたい場合、抽象クラスに「APIへリクエストする」「応答をパースする」といったメソッドを定義し、継承先ごとに実際の通信先やパース処理を変えるだけにすると便利です。

こうすることで共通の呼び出しパターンを維持しながら、異なるAPIに対応できるようになります。

継承が増えすぎるとコード全体の構造が複雑化する場合があります。
抽象クラスを設計するときは、実際に継承を必要とするクラスの数やメソッドの共通性を十分に検討しましょう。

抽象クラスを使うときの注意点

抽象クラスは柔軟な設計に役立ちますが、使いどころを間違えると逆にコードが複雑になりがちです。

以下の点に気をつけながら使ってみてください。

継承階層が深くならないようにする

抽象クラスを使うと、クラスの階層が増えやすくなります。

親クラス → 抽象クラス → 具体的なクラス

といった形で段階が増えると、コードの可読性やメンテナンス性が下がる恐れもあるのです。

階層が深すぎる設計はできるだけ避けて、わかりやすさを重視しましょう。

必要ない機能を詰め込みすぎない

共通化できるのは便利ですが、共通部分が多くなりすぎると**「本当にそこまで抽象クラスでまとめるべき?」** という問題が出てくることもあります。

プロジェクトの規模や人数、クラスの用途などを考慮して、抽象クラスに必要以上の機能を詰め込みすぎないように注意しましょう。

テストを含めたトータル設計を意識する

継承関係があるとテストコードも複雑になりがちです。

抽象クラスで定義しているメソッドをどのようにテストするか、どこまで自動化するかなど、全体の流れを考慮して設計するとスムーズに進められます。

こうした一連の流れを意識することで、実際のソフトウェア開発で役立つ抽象クラスの使い方を身につけられるでしょう。

抽象クラスとインターフェースの違い

初心者の方であれば、「抽象クラス」と「インターフェース」の違いがあいまいに感じられるかもしれません。

他の言語などでは「インターフェースを使って定義する」というやり方もありますが、Pythonでは抽象クラスを使う形がその代わりになることが多いです。

  • インターフェース:メソッド名や引数、戻り値などの定義のみを行い、クラス自体には実装コードを含まないもの
  • 抽象クラス:インターフェースのように抽象メソッドだけを定義することもできるし、一部メソッドの実装を含むこともできる

Pythonでは明示的なキーワードでインターフェースを定義する方法はありません。

その代わり、abc モジュールで抽象クラスを作る方法がインターフェースに近い働きをします。

つまり「抽象メソッドだけを定義して、継承先に実装を強制する」という利用パターンは、まさにインターフェースのように機能すると考えてみてください。

まとめ

抽象クラスは、クラス間で共通化したい部分をしっかりと押さえつつ、実際の具体的な実装は継承先に任せるという考え方がポイントになります。

Python では abc モジュールの ABCMeta@abstractmethod を使うだけなので、基本的には比較的とっつきやすいかもしれません。

ただ、やみくもに抽象クラスを導入するのではなく、

  • 必須となるメソッドをどれほど共通化できるか
  • クラス同士の関連が複雑にならないか といった点を踏まえながら使うと、本当に便利な設計を実現できます。

皆さんも、同じような性質を持つクラスを複数作りたい場面や、一貫したインターフェースを提供したい場面で活用してみてはいかがでしょうか。

Pythonをマスターしよう

この記事で学んだPythonの知識をさらに伸ばしませんか?
Udemyには、現場ですぐ使えるスキルを身につけられる実践的な講座が揃っています。