抽象基底クラス ABC(abstract base class)
【 目次 】
抽象基底クラスはpythonにおける抽象クラスを作成するためのメタクラスである。
抽象クラスとは実装すべきメソッドやプロパティを抽象メソッドや抽象プロパティとして定義したインスタンス化できないクラスである。
そして、抽象クラスを継承したクラスでは抽象クラスで定義されている抽象メソッドやプ抽象プロパティを実装しない限りそのクラスも抽象クラスとなりインスタンス化できない。
抽象クラスはJavaやC#におけるインターフェースの役割を果たし、抽象クラスを継承したクラスでは必要なメソッドの実装が保障されることになる。
メタクラスを使って抽象クラスを実装する
抽象クラスを実装するには、クラスのメタクラスにabcモジュールの抽象基底クラスABCMetaクラスを指定する必要がある。
しかし、メタクラスにABCMetaクラスを指定しただけではインスタンスの作成時にエラーは発生しないようである。
import abc class AbsMyCls(object): __metaclass__ = abc.ABCMeta def method(self): print u"抽象クラスのメソッドなのにメソッドが実行できました。" my_inst=AbsMyCls() my_inst.method()
実行結果
抽象クラスのメソッドなのにメソッドが実行できました。
インスタンスの作成時にエラーは発生させるためには、1つ以上の抽象メソッドか抽象プロパティを指定する必要がある。
抽象メソッド
抽象メソッドの定義はデコレータ@abc.abstractmethodをメソッドに指定する事で定義する。
import abc class AbsMyCls(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def abc_method(self): pass
抽象メソッドが定義されていると
my_inst=AbsMyCls()
抽象クラスはインスタンスが作成できないのでエラーが発生。
実行結果
TypeError: Can't instantiate abstract class MyCls with abstract methods abc_property
抽象クラスを継承したクラス(具象クラス)を作成してabc_methodメソッドを実装。
class MyCls(AbsMyCls): def abc_method(self): print u"abc_methodを実行" my_inst=MyCls() my_inst.abc_method()
具象クラスにてメソッドを実装したので実行可能に。
実行結果
abc_methodを実行
抽象プロパティ
抽象クラスには抽象プロパティを定義する事もできる。
抽象プロパティを定義するにはabcモジュールのabstractproperty関数もしくはデコーレータ@abstractpropertyを使う。
abstractproperty関数の例
import abc class AbsMyCls(object): __metaclass__ = abc.ABCMeta def get_abc_property(self): pass def set_abc_property(self,value): pass abc_property = abc.abstractproperty(get_abc_property, set_abc_property) class MyCls(AbsMyCls): def __init__(self): self._abc_property="default value" def get_abc_property(self): return self._abc_property def set_abc_property(self,value): self._abc_property=value abc_property = property(get_abc_property, set_abc_property) my_inst=MyCls() my_inst.abc_property="changed value" print my_inst.abc_property
実行結果
changed value
デコーレータの例
@abstractpropertyを使って読取り専用のプロパティだけを定義。
import abc class AbsMyCls(object): __metaclass__ = abc.ABCMeta @abc.abstractproperty def abc_property(self): pass class MyCls(AbsMyCls): @property def abc_property(self): return u"プロパティ値" my_inst=MyCls() print my_inst.abc_property
実行結果
プロパティ値
抽象クラスで定義した抽象プロパティを具象クラスで実装する場合にはあらためて@propertyなどの再指定が必要になるので注意。
具象クラスから抽象クラスの抽象メソッドを呼び出す
抽象クラスにてもととなる抽象メソッドをあらかじめ実装しておいて具象クラスでそれをそれを呼び出す事もできる。
import abc class AbsMyCls(object): __metaclass__ = abc.ABCMeta @abc.abstractmethod def abc_method(self): print u"abc_methodを実行" class MyCls(AbsMyCls): def abc_method(self): super(MyCls, self).abc_method() my_inst=MyCls() my_inst.abc_method()
実行結果
abc_methodを実行
クラスの定義後、後から動的に抽象メソッドを追加する事はできない
公式ドキュメントには 「クラスに動的に抽象メソッドを追加する、あるいはメソッドやクラスが作られた後から抽象的かどうかの状態を変更しようと試みることは、サポートされません。」とありabstractmethod関数を使って後から動的に抽象メソッドを追加する事はできないようだ。
import abc class AbsMyCls(object): __metaclass__ = abc.ABCMeta def func(self): print u"methodを実行" AbsMyCls.method=abc.abstractmethod(func) class MyCls(AbsMyCls): pass my_inst=MyCls() my_inst.method()
MyClsは抽象メソッドmethodを実装していないにもかかわらずエラーにならない。
実行結果
methodを実行
抽象クラスより具象クラスを実装するもう1つの方法 - 具象クラスを抽象クラスに登録
具象クラスを実装するもう1つの方法として、抽象クラスを継承して具象クラスを実装するのではなく、具象クラスを抽象クラスに登録する方法がある。
class MyCls(object): def abc_method(self): print u"methodを実行" AbsMyCls.register(MyCls)
registerメソッドを実行すると、具象クラスが抽象クラスを継承していなくとも、組込み関数issubclassやisinstanceは継承していると認識してTrueを返す。
print issubclass(MyCls, AbsMyCls) print isinstance(my_inst, AbsMyCls)
実行結果
True True
具象クラスが抽象メソッドを実装していなくとも、registerメソッドではエラーは発生しない。 そして、メソッドの実行時に初めてエラーが発生する事になる。
class MyCls(object): pass AbsMyCls.register(MyCls) my_inst=MyCls() my_inst.abc_method()
実行結果
Traceback (most recent call last): File "xxx.py", line xxx, in <module> my_inst.abc_method() AttributeError: 'MyCls' object has no attribute 'abc_method'