Pythonで抽象クラスを実装する
今回は、Pythonで抽象クラスを実装する方法を解説します。
抽象クラスとは?
プログラミング言語によりある程度違いはありますが、抽象クラスとは、以下の特徴を持ったクラスになります。
- 継承されることを前提としています。
- 空実装のメソッドが定義されています。(これを抽象メソッドと呼びます。)
- このクラスを継承したクラスは、抽象メソッドを再定義(オーバーライド)しなければなりません。
よって、抽象クラスは子クラスに対してメソッドの定義を強制する機能を持っています。
用途としては、多態性(ポリモーフィズム)を実装するときに、メソッドの実装漏れ、引数または戻り値の違いによるエラーの回避、などがあります。
実装方法
ここでは、抽象クラスと抽象メソッドの実装方法を解説します。
まず、抽象クラスの実装方法はABCMeta
というメタクラスを用います。抽象クラスにしたいクラスのメタクラスにABCMeta
を設定することにより、そのクラスが抽象クラスになります。(ちなみに、ABC
とはAbstract Base Class
の略だそうです。)
from abc import ABCMeta class vehicle(metaclass = ABCMeta): pass
また、PythonにはABC
というクラスがあり、このクラスを継承することでも抽象クラスを実現することが可能です。
from abc import ABC class vehicle(ABC): pass
このABC
の実態は、ABCMeta
をメタクラスに設定しているだけのクラスです。よって、初めのコードと実質同じです。
次に抽象メソッドを実装するときは、@abstractmethod
デコレータを使用します。このデコレータを付与されたメソッドが抽象メソッドになります。抽象メソッドには実際の処理を記述しないので、pass
またはraise NotImplementedError()
を実装しておきます。(NotImplementedError()
とは、未実装エラーを表す例外です。)
from abc import ABCMeta from abc import abstractmethod class vehicle(metaclass = ABCMeta): """抽象クラスvehicleの定義。""" @abstractmethod def start(self): raise NotImplementedError() @abstractmethod def stop(self): raise NotImplementedError()
ABCMeta
には、抽象メソッドが実装されている場合にインスタンスを生成しようとすると、例外が発生する仕様になっています。よって、上記vehicle
のインスタンスを生成しようとすると、以下のように例外が発生します。
v = vehicle()
# TypeError: Can't instantiate abstract class vehicle with abstract methods start, stop
なお、ABCMeta
をメタクラスとしているが抽象メソッドが実装されていないクラスや、抽象メソッドは実装されているがABCMeta
をメタクラスとしていないクラスのインスタンスは生成出来てしまいます。抽象クラスを実装するときは、ABCMeta
をメタクラスにすることと抽象メソッドの実装をセットで行ってください。
以下のように、抽象クラスを継承して抽象メソッドをオーバーライドすれば、インスタンスの生成が出来るようになります。
from abc import ABCMeta from abc import abstractmethod class vehicle(metaclass = ABCMeta): """抽象クラスvehicleの定義。""" @abstractmethod def start(self): raise NotImplementedError() @abstractmethod def stop(self): raise NotImplementedError() class car(vehicle): """vehicleを継承したクラスcarの定義。""" def start(self): print("car start.") def stop(self): print("car stop.") class motorcycle(vehicle): """vehicleを継承したクラスmotorcycleの定義。""" def start(self): print("moto start.") def stop(self): print("moto stop.") my_car = car() my_car.start() my_car.stop() my_motorcycle = motorcycle() my_motorcycle.start() my_motorcycle.stop() # car start. # car stop. # moto start. # moto stop.
抽象メソッドを表すデコレータについては、@abstractmethod
以外にも存在します。ここでは詳細を省きますが、抽象メソッドを表すデコレータを以下に列挙しておきます。
@abstractmethod
@abstractclassmethod
@abstractstaticmethod
@abstractproperty
ちなみに、誤って抽象クラスのインスタンスを生成してしまっているコードをflake8
にかけてみましたが、こちらではなにも表示されませんでした。抽象クラスのインスタンス生成エラーは実行時にしかわからないようです。
参考
-
公式ドキュメントです。
-
こちらはWikipediaの抽象クラスに関する説明です。