29.7. abc
— 抽象基底クラス¶
ソースコード: Lib/abc.py
このモジュールは Python に PEP 3119 で概要が示された 抽象基底クラス (ABC) を定義する基盤を提供します。なぜこれが Python に付け加えられたかについてはその PEP を参照してください。 (ABC に基づいた数の型階層を扱った PEP 3141 と numbers
モジュールも参照してください。)
collections.abc
サブモジュールには ABC から派生した具象クラスがいくつかあります。これらから、もちろんですが、さらに派生させることもできます。また collections
モジュールにはいくつかの ABC もあって、あるクラスやインスタンスが特定のインタフェースを提供しているかどうか、たとえばハッシュ可能かあるいはマッピングか、をテストできます。
このモジュールは、 抽象基底クラスを定義するためのメタクラス ABCMeta
と、 継承を利用して抽象基底クラスを代替的に定義するヘルパークラス ABC
を提供します。
-
class
abc.
ABC
¶ ABCMeta
をメタクラスとするヘルパークラスです。このクラスを使うと、混乱しがちなメタクラスを使わずに、単にABC
を継承するだけで抽象基底クラスを作成できます。例:from abc import ABC class MyABC(ABC): pass
ABC
の型はやはりABCMeta
であり、そのためABC
から継承するときは、メタクラスの衝突を引き起こし得る多重継承のような、メタクラスを使う上でのいつもの用心が求められることに注意してください。 metaclass キーワードを渡し、ABCMeta
を直接利用することで、抽象基底クラスを直接定義することもできます。例:from abc import ABCMeta class MyABC(metaclass=ABCMeta): pass
バージョン 3.4 で追加.
-
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 ABC class MyABC(ABC): 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(ABC): @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
が手に入るわけではないことに注意してください。それは別に提供されています。-
abc
モジュールは以下のデコレータも提供しています:
-
@
abc.
abstractmethod
¶ 抽象メソッドを示すデコレータです。
このデコレータを使うには、クラスのメタクラスが
ABCMeta
かそれを継承したものである必要があります。ABCMeta
の派生クラスをメタクラスに持つクラスは、全ての抽象メソッドとプロパティをオーバーロードしない限りインスタンス化することができません。抽象メソッドは通常の 'super' 呼び出し方法で呼ぶことができます。abstractmethod()
はプロパティやデスクリプタのために抽象メソッドを定義することもできます。クラスに動的に抽象メソッドを追加する、あるいはメソッドやクラスが作られた後から抽象的かどうかの状態を変更しようと試みることは、サポートされません。
abstractmethod()
が影響を与えるのは正規の継承により派生したサブクラスのみで、ABC のregister()
メソッドで登録された "仮想的サブクラス" は影響されません。abstractmethod()
が他のメソッドデスクリプタと組み合わされる場合、次の例のように、一番内側のデコレータとして適用しなければなりません:class C(ABC): @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
モジュールは以下のレガシーなデコレータも提供しています:
-
@
abc.
abstractclassmethod
¶ バージョン 3.2 で追加.
バージョン 3.3 で撤廃:
classmethod
をabstractmethod()
と一緒に使えるようになったため、このデコレータは冗長になりました。組み込みの
classmethod()
のサブクラスで、抽象クラスメソッドであることを示します。それ以外はabstractmethod()
と同じです。この特殊ケースは
classmethod()
デコレータが抽象メソッドに適用された場合に抽象的だと正しく認識されるようになったため撤廃されます:class C(ABC): @classmethod @abstractmethod def my_abstract_classmethod(cls, ...): ...
-
@
abc.
abstractstaticmethod
¶ バージョン 3.2 で追加.
バージョン 3.3 で撤廃:
staticmethod
をabstractmethod()
と一緒に使えるようになったため、このデコレータは冗長になりました。組み込みの
staticmethod()
のサブクラスで、抽象静的メソッドであることを示します。それ以外はabstractmethod()
と同じです。この特殊ケースは
staticmethod()
デコレータが抽象メソッドに適用された場合に抽象的だと正しく認識されるようになったため撤廃されます:class C(ABC): @staticmethod @abstractmethod def my_abstract_staticmethod(...): ...
-
@
abc.
abstractproperty
¶ バージョン 3.3 で撤廃:
property
、property.getter()
、property.setter()
およびproperty.deleter()
をabstractmethod()
と一緒に使えるようになったため、このデコレータは冗長になりました。組み込みの
property()
のサブクラスで、抽象プロパティであることを示します。この特殊ケースは
property()
デコレータが抽象メソッドに適用された場合に抽象的だと正しく認識されるようになったため撤廃されます:class C(ABC): @property @abstractmethod def my_abstract_property(self): ...
この例は読み出し専用のプロパティを定義しています。プロパティを構成するメソッドの1つ以上を abstract にすることで、読み書きできる抽象プロパティを定義することができます:
class C(ABC): @property def x(self): ... @x.setter @abstractmethod def x(self, val): ...
構成するメソッド全てが abstract でない場合、abstract と定義されたメソッドのみが、具象サブクラスによってオーバーライドする必要があります:
class D(C): @C.x.setter def x(self, val): ...
abc
モジュールは以下の関数も提供しています:
-
abc.
get_cache_token
()¶ 現在の抽象基底クラスのキャッシュトークンを返します。
このトークンは、仮想サブクラスのための抽象基底クラスの現在のバージョンを特定する (等価性検査をサポートしている) 不透明なオブジェクトです。 任意のABCでの
ABCMeta.register()
の呼び出しごとに、トークンは変更されます。バージョン 3.4 で追加.
脚注
[1] | C++ プログラマは Python の仮想的基底クラスの概念は C++ のものと同じではないということを銘記すべきです。 |