29.7. abc — 抽象基底クラス

ソースコード: Lib/abc.py


このモジュールは Python に PEP 3119 で概要が示された 抽象基底クラス (ABC) を定義する基盤を提供します。なぜこれが Python に付け加えられたかについてはその PEP を参照してください。 (ABC に基づいた数の型階層を扱った PEP 3141numbers モジュールも参照してください。)

collections.abc サブモジュールには ABC から派生した具象クラスがいくつかあります。これらから、もちろんですが、さらに派生させることもできます。また collections モジュールにはいくつかの ABC もあって、あるクラスやインスタンスが特定のインタフェースを提供しているかどうか、たとえばハッシュ可能かあるいはマッピングか、をテストできます。

このモジュールは以下のクラスを提供します:

class abc.ABCMeta

抽象基底クラス(ABC)を定義するためのメタクラス。

ABC を作るときにこのメタクラスを使います。ABC は直接的にサブクラス化することができ、ミックスイン(mix-in)クラスのように振る舞います。また、無関係な具象クラス(組み込み型でも構いません)と無関係な ABC を “仮想的サブクラス” として登録できます – これらとその子孫は組み込み関数 issubclass() によって登録した ABC のサブクラスと判定されますが、登録した ABC は MRO (Method Resolution Order, メソッド解決順)には現れませんし、この ABC のメソッド実装が(super() を通してだけでなく)呼び出し可能になるわけでもありません。 [1]

メタクラス ABCMeta を使って作られたクラスには以下のメソッドがあります:

register(subclass)

subclass を “仮想的サブクラス” としてこの ABC に登録します。たとえば:

from abc import ABCMeta

class MyABC(metaclass=ABCMeta):
    pass

MyABC.register(tuple)

assert issubclass(tuple, MyABC)
assert isinstance((), MyABC)

バージョン 3.3 で変更: クラスデコレータとして使うことができるように、登録されたサブクラスを返します。

バージョン 3.4 で変更: register() の呼び出しを検出するために、get_cache_token() 関数を使うことができます。

また、次のメソッドを抽象基底クラスの中でオーバーライドできます:

__subclasshook__(subclass)

(クラスメソッドとして定義しなければなりません。)

subclass がこの ABC のサブクラスと見なせるかどうかチェックします。これによって ABC のサブクラスと見なしたい全てのクラスについて register() を呼び出すことなく issubclass の振る舞いをさらにカスタマイズできます。 (このクラスメソッドは ABC の __subclasscheck__() メソッドから呼び出されます。)

このメソッドは True, False または NotImplemented を返さなければなりません。True を返す場合は、subclass はこの ABC のサブクラスと見なされます。False を返す場合は、subclass はたとえ通常の意味でサブクラスであっても ABC のサブクラスではないと見なされます。NotImplemented の場合、サブクラスチェックは通常のメカニズムに戻ります。

この概念のデモとして、次の ABC 定義の例を見てください:

class Foo:
    def __getitem__(self, index):
        ...
    def __len__(self):
        ...
    def get_iterator(self):
        return iter(self)

class MyIterable(metaclass=ABCMeta):

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    def get_iterator(self):
        return self.__iter__()

    @classmethod
    def __subclasshook__(cls, C):
        if cls is MyIterable:
            if any("__iter__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

MyIterable.register(Foo)

ABC MyIterable は標準的なイテラブルのメソッド __iter__() を抽象メソッドとして定義します。ここで与えられている抽象クラスの実装は、サブクラスから呼び出すことができます。 get_iterator() メソッドも MyIterable 抽象基底クラスの一部ですが、抽象的でない派生クラスはこれをオーバーライドする必要はありません。

ここで定義されるクラスメソッド __subclasshook__() の意味は、 __iter__() メソッドがクラスの(または __mro__ でアクセスされる基底クラスの一つの) __dict__ にある場合にもそのクラスが MyIterable だと見なされるということです。

最後に、一番下の行は Foo__iter__() メソッドを定義しないにもかかわらず MyIterable の仮想的サブクラスにします(Foo は古い様式の __len__()__getitem__() を用いたイテレータプロトコルを使っています。)。これによって Foo のメソッドとして get_iterator が手に入るわけではないことに注意してください。それは別に提供されています。

class abc.ABC

ABCMeta をメタクラスとするヘルパークラスです。 このクラスを使うと、混乱しがちなメタクラスを使わずに、単に ABC を継承するだけで抽象基底クラスを作成できます。

ABC の型はやはり ABCMeta であり、そのため ABC から継承するときは、メタクラスの衝突を引き起こし得る多重継承のような、メタクラスを使う上でのいつもの用心が求められることに注意してください。

バージョン 3.4 で追加.

abc モジュールは以下のデコレータも提供しています:

@abc.abstractmethod

抽象メソッドを示すデコレータです。

このデコレータを使うには、クラスのメタクラスが ABCMeta かそれを継承したものである必要があります。 ABCMeta の派生クラスをメタクラスに持つクラスは、全ての抽象メソッドとプロパティをオーバーロードしない限りインスタンス化することができません。抽象メソッドは通常の ‘super’ 呼び出し方法で呼ぶことができます。 abstractmethod() はプロパティやデスクリプタのために抽象メソッドを定義することもできます。

クラスに動的に抽象メソッドを追加する、あるいはメソッドやクラスが作られた後から抽象的かどうかの状態を変更しようと試みることは、サポートされません。 abstractmethod() が影響を与えるのは正規の継承により派生したサブクラスのみで、ABC の register() メソッドで登録された “仮想的サブクラス” は影響されません。

abstractmethod() が他のメソッドデスクリプタと組み合わされる場合、次の例のように、一番内側のデコレータとして適用しなければなりません:

class C(metaclass=ABCMeta):
    @abstractmethod
    def my_abstract_method(self, ...):
        ...
    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls, ...):
        ...
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(...):
        ...

    @property
    @abstractmethod
    def my_abstract_property(self):
        ...
    @my_abstract_property.setter
    @abstractmethod
    def my_abstract_property(self, val):
        ...

    @abstractmethod
    def _get_x(self):
        ...
    @abstractmethod
    def _set_x(self, val):
        ...
    x = property(_get_x, _set_x)

デスクリプタをABC 機構と協調させるために、デスクリプタは __isabstractmethod__ を使って自身が抽象であることを示さなければなりません。一般的に、この属性は、そのデスクリプタを構成するのに使われたメソッドのいずれかが抽象である場合に True になります。例えば、 Python 組み込みの property は、次のコードと透過に振る舞います:

class Descriptor:
    ...
    @property
    def __isabstractmethod__(self):
        return any(getattr(f, '__isabstractmethod__', False) for
                   f in (self._fget, self._fset, self._fdel))

注釈

Java の抽象メソッドと違い、これらの抽象メソッドは実装を持ち得ます。この実装は super() メカニズムを通してそれをオーバーライドしたクラスから呼び出すことができます。これは協調的多重継承を使ったフレームワークにおいて super 呼び出しの終点として有効です。

@abc.abstractclassmethod

組み込みの classmethod() のサブクラスで、抽象クラスメソッドであることを示します。それ以外は abstractmethod() と同じです。

この特殊ケースは classmethod() デコレータが抽象メソッドに適用された場合に抽象的だと正しく認識されるようになったため撤廃されます:

class C(metaclass=ABCMeta):
    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls, ...):
        ...

バージョン 3.2 で追加.

バージョン 3.3 で撤廃: classmethodabstractmethod() と一緒に使えるようになったため、このデコレータは冗長になりました。

@abc.abstractstaticmethod

組み込みの staticmethod() のサブクラスで、抽象静的メソッドであることを示します。それ以外は abstractmethod() と同じです。

この特殊ケースは staticmethod() デコレータが抽象メソッドに適用された場合に抽象的だと正しく認識されるようになったため撤廃されます:

class C(metaclass=ABCMeta):
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(...):
        ...

バージョン 3.2 で追加.

バージョン 3.3 で撤廃: staticmethodabstractmethod() と一緒に使えるようになったため、このデコレータは冗長になりました。

@abc.abstractproperty(fget=None, fset=None, fdel=None, doc=None)

組み込みの property() のサブクラスで、抽象プロパティであることを示します。

この関数を使うにはクラスのメタクラスが ABCMeta であるかまたは派生したものであることが求められます。 ABCMeta から派生したメタクラスを持つクラスは全ての抽象メソッドおよびプロパティをオーバーライドしない限りインスタンス化できません。抽象プロパティは普通の ‘super’ 呼び出し機構を使って呼び出すことができます。

この特殊ケースは property() デコレータが抽象メソッドに適用された場合に抽象的だと正しく認識されるようになったため撤廃されます:

class C(metaclass=ABCMeta):
    @property
    @abstractmethod
    def my_abstract_property(self):
        ...

この例は読み取り専用のプロパティを定義しています。プロパティを構成するメソッドの1つ以上を abstract にすることで、読み書きできる抽象プロパティを定義することができます:

class C(metaclass=ABCMeta):
    @property
    def x(self):
        ...

    @x.setter
    @abstractmethod
    def x(self, val):
        ...

構成するメソッド全てが abstract でない場合、abstract と定義されたメソッドのみが、具象サブクラスによってオーバーライドする必要があります:

class D(C):
    @C.x.setter
    def x(self, val):
        ...

バージョン 3.3 で撤廃: propertyproperty.getter()property.setter() および property.deleter()abstractmethod() と一緒に使えるようになったため、このデコレータは冗長になりました。

abc モジュールは以下の関数も提供しています:

abc.get_cache_token()

現在の抽象基底クラスのキャッシュトークンを返します。

このトークンは、仮想サブクラスのための抽象基底クラスの現在のバージョンを特定する (等価性検査をサポートしている) 不透明なオブジェクトです。 任意のABCでの ABCMeta.register() の呼び出しごとに、トークンは変更されます。

バージョン 3.4 で追加.

脚注

[1]

C++ プログラマは Python の仮想的基底クラスの概念は C++ のものと同じではないということを銘記すべきです。