Address
304 North Cardinal St.
Dorchester Center, MA 02124

Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM

Pythonの抽象基底クラスABCの解説

Pythonの抽象基底クラス(ABC)の基本を解説。その使い方やメリットは?

PythonのABC(抽象基底クラス)の基本を解説。その使い方やメリットは?

今回はPythonにおいてわかりにくい概念・機能の代表?でもある抽象基底クラス(abstract base class:abcまたはABC)について、特に抽象基底クラスを作るためのモジュールであるABCモジュールについて簡単に解説します。

なおこの記事における用語の使い方としては、

  • 抽象基底クラス(abstract base class)という概念・機能そのもの→ABC
  • 抽象基底クラスを生成するためのモジュールとしてのabc→abc
  • そのabcモジュール内に定義されたクラスとしてのABC→abc.ABC

基本的にこのようにします。

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

公式ドキュメント:abstract base class

ABCはインターフェイスを定義する方法の1つ

Pythonにおける抽象基底クラス(ABC)とは、Pythonにおいてインターフェイスを実装するための1つの手段・機能です。

それではインターフェイスとは何でしょう?

インターフェイスとは?

プログラミングの世界におけるインターフェイスとは、大雑把にいえばクラスが持つべきデータ(属性)やメソッドを定義したものです。それを表したイメージ図が下の図です

抽象基底クラスのイメージ図

Pythonの抽象基底クラスABCの解説

よくプログラミングの初心者向け解説ではクラス(class)を設計図だと説明されます。そこでこのイメージ図でもそんな業界の慣習?にあわせて、設計図としてクラスA〜Cまでを示しています。その各クラスからそれぞれの製品として各インスタンスが生成されます。

ですが、いろいろな設計図にもやはり書き方のルールというものがそれぞれの企業または業界ごとにあると思います。

そうした「設計図を作るときのルール」、それが抽象基底クラス(abstract base class)だというイメージです。 

Pythonにおけるインターフェイス

そして現時点(2024年)において、実はPythonには他のプログラミング言語でいうところのこのインターフェイスという機能・仕組みは直接的には導入されていません。しかし、インターフェイスがないPythonでも、インターフェイスを事実上・間接的に実現することができる方法が用意されています。その方法の1つがABCなのです。

それではさっそくその初歩的な使用例と特徴を見ていきましょう。

基本的な使用例と特徴

ABCの作り方と@abstractmethodデコレータ

ABCの作り方は大きくわけて次の2通りあります。こちらの使用例をみてください。

#### 作り方1: abcモジュールを使う ############################
from abc import ABC, abstractmethod

class MyABC_1(ABC):
    # デコレータとして使う
    # デコレイトされる関数(f_1)の中身は基本的に定義しない
    @abstractmethod
    def f_1(self):
        pass


#### 作り方2: メタクラスを指定する ############################
from abc import ABCMeta

class MyABC_2(metaclass=ABCMeta):
    @abstractmethod
    def f_2(self):
        pass

Pythonではこうして2つの方法で抽象的クラス(上ではMyABC_1とMyABC_2)を作ることができます。

そしてどちらもメソッドに@abstractmethodというデコレータを使っています。抽象基底クラスにはこのデコレータを使うことが基本となります。

作り方1のようにabcモジュールからabc.ABCを承継するが便利でわかりやすいと思います。またそれで十分だと思います。したがって以下ではabc.ABCを承継する作り方1の方法を前提とした解説をしていきます。

特徴1:抽象基底クラスで@abstractmethodを使うと直接にインスタンスを作れない

この抽象基底クラスも当然クラスです。そしてクラスといえばインスタンスを作って使うのが基本となるわけですが、実は抽象基底クラスからはインスタンスを直接つくることはできません。下の画像をみてください。

Pythonの抽象基底クラスABCについて

矢印①において、2つの抽象基底クラスからそれぞれインスタンスを作ろうとしていますが、まずその部分のコードの下に波線部があらわれます。これはVSCode上で「そうしたコードはおかしいですよ」というメッセージです。

そしてその波線部にマウスカーソルを合わせると矢印②のような警告メッセージが実際に現れます。

上で作った2つの抽象基底クラスにはそれぞれ@abstractmethodデコレータが使われています。これら2つの抽象基底クラスからインスタンスを作ってみます。

insta_1 = MyABC_1()
insta_2 = MyABC_2()

これを実行すると、次のようにエラーがでます。

>>> insta_1 = MyABC_1()

Traceback (most recent call last):

File “<stdin>”, line 1, in <module>

TypeError: Can’t instantiate abstract class MyABC_1 with abstract method f_1

>>> insta_2 = MyABC_2()

Traceback (most recent call last):

File “<stdin>”, line 1, in <module>

TypeError: Can’t instantiate abstract class MyABC_2 with abstract method f_2

@asbtractmehtodデコレータを使ったABCではこのように直接インスタンスを作ることができません。しかし同デコレータを使わなければ可能です。

特徴2:@abstractmethodは、メソッド定義の忘れを予防

上で使用した@abstractmethodデコレータは、メソッドを抽象的メソッド(抽象的メソッドと言葉は私が勝手に作った言葉です)へと変更する役割を持ちます。

抽象的メソッドになったメソッドは、ABCのサブクラスにおいてそのメソッドを定義しないと例外が発生します。

たとえば、

class SubClass_1(MyABC_1):
    # def f_1(self):
    #     pass
    def f_3(self):
        pass

insta_3 = SubClass_1()

このコードでは、

  • MyABC_1を承継したサブクラスをSub_Class_1を定義
  • しかし、親クラスMyABC_1で定義したメソッドdef f_1は定義していない

というものです。

このコードをVSCodeで書くと、

Pythonの抽象基底クラスABCについて

このように波線部の警告が登場します。これは、抽象的クラスMyABC_1で定義し、@abstractmethodで抽象的メソッドとしたdef f_1をそのサブクラスで定義していないからです。

そしてそのまま実行すれば、次のようにエラーとなります。

Traceback (most recent call last):
File “”, line 1, in
TypeError: Can’t instantiate abstract class SubClass_1 with abstract method f_1

もちろん、上コードからコメントアウトを外して次のように書けば例外は発生しません。

class SubClass_1(MyABC_1):
    def f_1(self):
        pass
    def f_3(self):
        pass

insta_3 = SubClass_1()

@abstractmethodデコレータはこのようにサブクラスで必ず実装しなければならないメソッドを実装し忘れることを防いでくれるわけですね。忘れずに、◯◯メソッドを定義しろ!という示してくれるわけです。

抽象基底クラスを使うメリット

以上ものすごく簡単に抽象的クラス(ABC)についてその基本を解説しましたが、つまるところ抽象基底クラスをメリットは、

  • Pythonでインターフェースを事実上実装できる
  • (特に@abstractmethodを使った場合に)メソッドの定義忘れなどのミスを減らせる

という2つが大きなものとなるでしょう。

注意点

なお実際の現場では、抽象基底クラス(ABC)を使うことはフレームワークそのものをゼロから開発する場合や大規模な開発をしたりする場合などを除けば、あまり使わないほうが良いとされているようです。

深く考えずになんでもかんでも抽象化したり、コードの再利用性だけを追求したりするのはコード全体の理解を妨げることにつながりかねないリスクがあるからです。

愛を分かち合いましょう