@Oddthinkingの答えは間違いではありませんが、Pythonがダックタイピングの世界でABCを持っている本当の、実際的な理由を見逃していると思います。
抽象メソッドはきちんとしていますが、私の意見では、ダックタイピングでまだカバーされていないユースケースを実際に満たすことはありません。抽象基本クラスの真の力は、との動作をカスタマイズできるようにする方法にisinstance
ありissubclass
ます。(__subclasshook__
基本的には、Python__instancecheck__
と__subclasscheck__
フックに加えてより使いやすいAPIです。)組み込みの構造をカスタムタイプで機能するように適合させることは、Pythonの哲学の大部分を占めています。
Pythonのソースコードは模範的なものです。標準ライブラリでの定義方法は次のとおりcollections.Container
です(執筆時点)。
class Container(metaclass=ABCMeta):
__slots__ = ()
@abstractmethod
def __contains__(self, x):
return False
@classmethod
def __subclasshook__(cls, C):
if cls is Container:
if any("__contains__" in B.__dict__ for B in C.__mro__):
return True
return NotImplemented
この定義は__subclasshook__
、属性を持つクラスは、__contains__
直接サブクラス化されていなくても、Containerのサブクラスと見なされることを示しています。だから私はこれを書くことができます:
class ContainAllTheThings(object):
def __contains__(self, item):
return True
>>> issubclass(ContainAllTheThings, collections.Container)
True
>>> isinstance(ContainAllTheThings(), collections.Container)
True
言い換えれば、適切なインターフェースを実装すれば、あなたはサブクラスになります!ABCは、ダックタイピングの精神に忠実でありながら、Pythonでインターフェイスを定義する正式な方法を提供します。さらに、これはオープンクローズ原則を尊重する方法で機能します。
Pythonのオブジェクトモデルは、表面的にはより「従来の」オブジェクト指向システム(Java *を意味します)のモデルと似ていますが、Yerクラス、Yerオブジェクト、Yerメソッドがありますが、表面をスクラッチすると、はるかに豊富なものが見つかります。より柔軟に。同様に、Pythonの抽象基本クラスの概念はJava開発者には認識できるかもしれませんが、実際には、それらは非常に異なる目的を目的としています。
単一のアイテムまたはアイテムのコレクションに作用できるポリモーフィック関数を書いていることに気付くことがありますが、同等のブロックisinstance(x, collections.Iterable)
よりもはるかに読みやすいことがわかります。(Pythonを知らなかった場合、コードの意図を最も明確にするのはこれら3つのうちどれですか?)hasattr(x, '__iter__')
try...except
そうは言っても、自分でABCを作成する必要はめったになく、通常はリファクタリングを通じてその必要性を発見します。多くの属性チェックを行うポリモーフィック関数、または同じ属性チェックを行う多くの関数を見ると、その匂いは抽出されるのを待っているABCの存在を示唆しています。
*Javaが「従来の」オブジェクト指向システムであるかどうかについての議論に入る必要はありません...
補遺:抽象基本クラスはisinstance
との動作をオーバーライドできますが、issubclass
それでも仮想サブクラスのMROには入りません。isinstance(x, MyABC) == True
これは、クライアントにとって潜在的な落とし穴です。で定義されたメソッドを持つすべてのオブジェクトではありませんMyABC
。
class MyABC(metaclass=abc.ABCMeta):
def abc_method(self):
pass
@classmethod
def __subclasshook__(cls, C):
return True
class C(object):
pass
# typical client code
c = C()
if isinstance(c, MyABC): # will be true
c.abc_method() # raises AttributeError
残念ながら、これは「それをしない」トラップの1つです(Pythonには比較的少ないです!):a__subclasshook__
メソッドと非抽象メソッドの両方でABCを定義することは避けてください。さらに、__subclasshook__
ABCが定義する一連の抽象メソッドと一貫性のある定義を作成する必要があります。