抽象クラスについて

10.6 抽象クラスについて

これまで様々な機能を持たせたクラスや、既存のクラスを利用して新しいクラスを作成してきました。さらにPythonでは通常のクラスとは少し異なった抽象クラス(abstract class)を作成し利用する事ができます。
抽象クラスとは、文字通り抽象的存在のクラスであり、具体的な処理は抽象クラスを継承したクラスに記述します。抽象クラスの存在意義は複数のクラスに対して強制的に共通機能(共通処理)を持たせることです。
次項より抽象クラスについて詳しく説明を行っていきます。

10.6.1 抽象クラスの特徴

通常のクラスとは異なる、抽象クラスの特徴やメリットについて以下に示します。

抽象クラスのメイン特徴

  • 抽象クラスのインスタンス生成は行えない。
  • 抽象クラスには処理内容を持たない、抽象メソッド(abstract method)を定義できる。
  • 抽象メソッドがある抽象クラスを継承したサブクラスは、抽象メソッドを必ずオーバーライドしなければなりません。(オーバーライドしないとコンパイラエラーになります。)

抽象クラスのその他特徴

  • 抽象メソッドを定義したクラスは、必ず抽象クラスとして定義する必要がある。(抽象メソッドが定義できるのは抽象クラスの機能の為)
  • 抽象メソッドを定義していない、抽象クラスも定義する事はできる。(抽象メソッドを定義しないと、通常のクラスと変わらなくなるため、このような定義はあまり意味がありません。)

抽象クラスのメリット

  • 継承を利用して共通処理を提供できる。
  • 抽象メソッドを定義する事で、継承した各サブクラスに必須のメソッド定義(名前、戻り値の型、引数の形式)を強制できる。

上記で示したメリットだけでは、言葉の意味はわかっても実際に理解は難しいと思います。
実際の現場での体験談で説明すると、例えば抽象クラスを用意せずに、ある人にこのような機能を必ず実装しなさいと、文章や口頭で説明を行っても指示の勘違いでの実装ミスや、機能の実装漏れなどの問題が起こる可能性があります。そこで抽象クラスを用意しておき、ある機能作成する場合「この抽象クラスを継承し実装しなさい」と指示を出すだけで、一般メソッドとして共通機能を提供でき、サブクラスごとの個別機能は抽象メソッドとして用意しておけば、各サブクラスを実装時には強制的に記述させることができるようになります。

図 10.6.1: 抽象クラスを元にした拡張イメージ

抽象クラスはインスタンス化できない特性の為、図2.1.1で示すように継承して初めてその真価を発揮します。継承することでサブクラスに共通処理を提供し、必須の処理(メソッド)を忘れずに実装させる事が可能になります。

10.6.2 抽象クラスの利用方法

プログラムでどのように記述すれば、抽象クラスを利用できるのかその方法について説明していきます。

抽象クラスを定義する方法について

抽象クラスはインスタンス化できない特殊なクラスの為、継承して利用する事が大前提になります。
まずは抽象クラスを定義する基本構文を以下に示します。

書式:抽象クラス定義

抽象クラスを定義するためには、abcモジュールをインポートしなければなりません。定義する際に、引数に「metaclass=abc.ABCMeta」を指定することで抽象クラスになります。
また抽象クラス内で、デコレータとして「@abc.abstractmethod」を指定されたメソッドは、抽象メソッドになります。

凡例:抽象クラス定義

抽象クラスの定義は、引数に「metaclass=abc.ABCMeta」を指定する以外、通常のクラスを定義する方法と同じです。

ポイント

  • 抽象クラスを定義するには引数に「metaclass=abc.ABCMeta」を指定する。さらに抽象メソッドにはpassだけを記述する。

凡例:抽象クラスの継承

抽象クラスを継承する方法は通常のクラスを継承する方法と同じで、継承元のスーパークラスが通常のクラスから抽象クラスに変わるだけです。但し抽象メソッドが定義されていると、サブクラスでは必ずオーバーライドしてメソッドを定義しないといけません。

上記凡例で示したnotePcクラスは、Computerクラスを継承しています。Computerクラスには抽象メソッド「osMemory()」が定義されているので、サブクラスであるnotePc内でosMemory ()メソッドをオーバーライドしています。この抽象メソッドのオーバーライドを行わないとコンパイルエラーになるので注意が必要です。

ポイント

  • 抽象クラスを継承する場合、抽象メソッドが定義されていれば必ずサブクラス内でオーバーライドする必要がある。

では、次の項で抽象クラスを継承した新しいクラスを利用するプログラムを紹介します。

10.6.3 抽象クラスを継承した新しいクラスを利用するプログラム

抽象クラスを継承して新しいクラスを作成し、継承元(スーパークラス)の機能も自身のクラスの機能も正しく利用できる事を確認します。

ソースコード

ソース・フォルダー: /Desktop/Python基礎講座
ファイル名: 第10章.ipynb
アクセスURL: http://localhost:8888/notebooks/Desktop/Python基礎講座/第10章.ipynb

    import abc

    class Animal(metaclass=abc.ABCMeta):

        #抽象メソッドの定義
        @abc.abstractmethod
        def bark(self):
            pass

    class Dog(Animal):

        def bark(self):
            print('ワン')

    class Cat(Animal):

        def bark(self):
            print('ニャー')

    class Pig(Animal):

        def bark(self):
            print('ブー、ブー')

    dog1 = Dog()
    dog1.bark()
    cat1 = Cat()
    cat1.bark()
    pig1 = Pig()
    pig1.bark()
	

実行結果

	ワン
	ニャー
	ブー、ブー
	

解説

3~8行目で抽象クラスAnimalを定義し、クラス内に抽象メソッドbark()を定義してます。

    class Animal(metaclass=abc.ABCMeta):

        #抽象メソッドの定義
        @abc.abstractmethod
        def bark(self):
            pass
	

10~23行目でAnimalクラスを継承した、Dogクラス、Catクラス、Pigクラスを定義してます。それぞれ抽象メソッド bark()をオーバーライドしなければならないため、以下のようにbark()メソッドを各クラス内で定義してます。

    class Dog(Animal):

        def bark(self):
            print('ワン')

    class Cat(Animal):

        def bark(self):
            print('ニャー')

    class Pig(Animal):

        def bark(self):
            print('ブー、ブー')
	

図 10.6.2: 抽象メソッドのオーバーロード

25~30行目でDogクラス、Catクラス、Pigクラスのそれぞれからインスタンスを生成しており、生成した各インスタンスからbark()メソッドを呼び出してます。

    dog1 = Dog()
    dog1.bark()
    cat1 = Cat()
    cat1.bark()
    pig1 = Pig()
    pig1.bark()
	

実行結果からオーバーロードから反映されていることがわかります。


NEXT>> 10.7 本章のまとめ