Pythonの抽象クラスとインターフェースの違い


回答:


618

時々表示されるのは次のとおりです。

class Abstract1( object ):
    """Some description that tells you it's abstract,
    often listing the methods you're expected to supply."""
    def aMethod( self ):
        raise NotImplementedError( "Should have implemented this" )

Pythonには正式なインターフェースコントラクトがない(そして必要ない)ため、抽象化とインターフェースのJavaスタイルの区別は存在しません。誰かが正式なインターフェースを定義しようと努力すれば、それも抽象クラスになります。唯一の違いは、ドキュメント文字列に記載されている意図にあります。

そして、抽象型とインターフェースの違いは、あなたがアヒルのタイピングをしているときのヘアスプリットです。

Javaは多重継承がないため、インターフェースを使用します。

Pythonには多重継承があるため、次のようなものも表示される場合があります

class SomeAbstraction( object ):
    pass # lots of stuff - but missing something

class Mixin1( object ):
    def something( self ):
        pass # one implementation

class Mixin2( object ):
    def something( self ):
        pass # another

class Concrete1( SomeAbstraction, Mixin1 ):
    pass

class Concrete2( SomeAbstraction, Mixin2 ):
    pass

これは、ミックスインを含む一種の抽象スーパークラスを使用して、素である具象サブクラスを作成します。


5
S.ロット、ダックタイピングのために、has-a(インターフェース)とis-a(継承)の区別は重要ではないということですか?
ロレンツォ

3
抽象型とインターフェースの違いは、アヒルのタイピングをするときのヘアスプリットです。「実質的」の意味がわかりません。デザインの観点から見ると、それは「本物」です。しかし、言語の観点からはサポートがない場合があります。規約を採用して、Pythonで抽象クラスとインターフェースクラス定義を区別できます。
S.Lott、2009

26
@ L.DeLeo-has-a対is-aの概念は正しいですか?私は一般的に、その区別をhas-a =メンバー変数とis-a =継承(親クラスまたはインターフェース)と見なしています。Javaで比較可能またはリストだと思います。これらは、インターフェースであるか抽象クラスであるかに関係なく、is-a関係です。
dimo414 2012年

43
NotImplementedError("Class %s doesn't implement aMethod()" % (self.__class__.__name__))より有益なエラーメッセージです:)
naught101

9
@Lorenzo a has-a関係は、継承、ダックタイピング、インターフェース、および抽象クラスとは何の関係もありません(4つすべてがis-a関係を参照)。
カールリヒター

197

Pythonの抽象クラスとインターフェイスの違いは何ですか?

オブジェクトのインターフェースは、そのオブジェクトのメソッドと属性のセットです。

Pythonでは、抽象基本クラスを使用してインターフェイスを定義および適用できます。

抽象基本クラスの使用

たとえば、collectionsモジュールの抽象基本クラスの1つを使用するとします。

import collections
class MySet(collections.Set):
    pass

これを使用しようとすると、TypeError作成したクラスが予想されるセットの動作をサポートしていないため、を取得します。

>>> MySet()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MySet with abstract methods
__contains__, __iter__, __len__

だから我々は、で実装するのに必要とされる以上 __contains____iter____len__ドキュメントからこの実装例を使用してみましょう:

class ListBasedSet(collections.Set):
    """Alternate set implementation favoring space over speed
    and not requiring the set elements to be hashable. 
    """
    def __init__(self, iterable):
        self.elements = lst = []
        for value in iterable:
            if value not in lst:
                lst.append(value)
    def __iter__(self):
        return iter(self.elements)
    def __contains__(self, value):
        return value in self.elements
    def __len__(self):
        return len(self.elements)

s1 = ListBasedSet('abcdef')
s2 = ListBasedSet('defghi')
overlap = s1 & s2

実装:抽象基本クラスの作成

メタクラスをに設定し、関連するメソッドでデコレーターabc.ABCMetaを使用して、独自の抽象基本クラスを作成できますabc.abstractmethod。メタクラスは装飾された関数を__abstractmethods__属性に追加し、定義されるまでインスタンス化を防ぎます。

import abc

たとえば、「有効」は、言葉で表現できるものとして定義されます。Python 2で、効率的な抽象基本クラスを定義したいとします。

class Effable(object):
    __metaclass__ = abc.ABCMeta
    @abc.abstractmethod
    def __str__(self):
        raise NotImplementedError('users must define __str__ to use this base class')

またはPython 3では、メタクラス宣言がわずかに変更されています。

class Effable(object, metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def __str__(self):
        raise NotImplementedError('users must define __str__ to use this base class')

ここで、インターフェイスを実装せずに有効なオブジェクトを作成しようとすると、次のようになります。

class MyEffable(Effable): 
    pass

そしてそれをインスタンス化しようとします:

>>> MyEffable()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class MyEffable with abstract methods __str__

仕事が終わっていないと言われています。

ここで、期待されるインターフェースを提供することで準拠する場合:

class MyEffable(Effable): 
    def __str__(self):
        return 'expressable!'

その後、抽象クラスから派生したクラスの具象バージョンを使用できます。

>>> me = MyEffable()
>>> print(me)
expressable!

既にこれらのインターフェイスを実装している仮想サブクラスを登録するなど、これを使ってできることは他にもありますが、それはこの質問の範囲を超えていると思います。abcただし、ここで説明する他の方法では、モジュールを使用してこの方法を適応させる必要があります。

結論

抽象基本クラスの作成がPythonのカスタムオブジェクトのインターフェースを定義することを示しました。


101

Python> = 2.6には、抽象基本クラスがあります。

抽象基本クラス(略称ABC)は、hasattr()などの他の手法が不格好な場合にインターフェースを定義する方法を提供することにより、ダックタイピングを補完します。Pythonには、データ構造(collectionsモジュール内)、数値(numbersモジュール内)、およびストリーム(ioモジュール内)用の多くの組み込みABCが付属しています。abcモジュールで独自のABCを作成できます。

Zopeインターフェースモジュールもあり、これはツイストのようにzopeの外部のプロジェクトで使用されます。私はそれで本当に慣れていないんだけど、wikiページがありますここにいるかもしれないのヘルプが。

一般に、抽象クラスの概念やPythonのインターフェースは必要ありません(編集-詳細はS.Lottの回答を参照)。


2
PythonでABCを使用することで何が得られますか?
CpILL

38

Pythonにはどちらの概念もありません。

それはインターフェイスの必要性を取り除いたダックタイピングを使用します(少なくともコンピューターでは:-))

Python <= 2.5:基本クラスは明らかに存在しますが、メソッドを「純粋仮想」としてマークする明示的な方法がないため、クラスは実際には抽象的ではありません。

Python> = 2.6:抽象基本クラスが存在ますhttp://docs.python.org/library/abc.html)。また、サブクラスで実装する必要があるメソッドを指定できます。構文はあまり好きではありませんが、機能はあります。ほとんどの場合、「使用する」クライアント側からダックタイピングを使用する方が良いでしょう。


3
Python 3.0は実際の抽象基本クラスを追加します。これらは、コレクションモジュールおよびその他の場所で使用されます。docs.python.org/3.0/library/abc.html
Lara Dougan

ダックタイピングがインターフェースの必要性をなくす理由についてのリファレンスは役に立ちます。オブジェクトにメソッドまたは属性を「突く」機能として理解しているダックタイピングは、必要な動作を指定する必要がないことをコンパイラに通知することを意味していることは明らかではありません。それらを実装する)、これは私が抽象基本クラスを理解する方法です。
Reb.Cabin

アヒルのタイピングではなく、Javaが描いたインターフェースと抽象クラスの間の不自然な線を取り除く多重継承のサポートです。
masi

35

説明するためのより基本的な方法:インターフェースは、空のマフィン鍋のようなものです。これは、コードを持たない一連のメソッド定義を含むクラスファイルです。

抽象クラスも同じですが、すべての関数を空にする必要はありません。いくつかはコードを持つことができます。完全に空ではありません。

区別する理由:Pythonには実用的な違いはあまりありませんが、大規模なプロジェクトの計画レベルでは、コードがないため、インターフェースについて話す方が一般的かもしれません。特に、この用語に慣れているJavaプログラマーと作業している場合はなおさらです。


ABCが独自の実装を持つことができる区別のための+
1-これは

17

一般に、インターフェイスは、単一継承クラスモデルを使用する言語でのみ使用されます。これらの単一継承言語では、いずれかのクラスが特定のメソッドまたはメソッドのセットを使用できる場合、通常はインターフェイスが使用されます。また、これらの単一継承言語では、抽象クラスを使用して、メソッドなしまたは複数のメソッドに加えてクラス変数を定義するか、単一継承モデルを利用して、一連のメソッドを使用できるクラスの範囲を制限します。

多重継承モデルをサポートする言語は、インターフェイスではなく、クラスまたは抽象基本クラスのみを使用する傾向があります。Pythonは多重継承をサポートしているため、インターフェイスを使用せず、基本クラスまたは抽象基本クラスを使用する必要があります。

http://docs.python.org/library/abc.html


2

抽象クラスは、1つ以上の抽象メソッドを含むクラスです。抽象メソッドに加えて、抽象クラスは静的メソッド、クラスメソッド、およびインスタンスメソッドを持つことができます。しかし、インターフェースの場合、それは他ではなく抽象的なメソッドのみを持ちます。したがって、抽象クラスの継承は必須ではありませんが、インターフェースの継承は必須です。


1

完全を期すために 、ABCが導入され、インターフェイスと比較されたPEP3119、およびオリジナルのTalinのコメントに言及する必要があります。

抽象クラスは完全なインターフェースではありません:

  • 継承階層に属している
  • 変更可能です

しかし、あなたがそれをあなた自身の方法で書くことを検討するならば:

def some_function(self):
     raise NotImplementedError()

interface = type(
    'your_interface', (object,),
    {'extra_func': some_function,
     '__slots__': ['extra_func', ...]
     ...
     '__instancecheck__': your_instance_checker,
     '__subclasscheck__': your_subclass_checker
     ...
    }
)

ok, rather as a class
or as a metaclass
and fighting with python to achieve the immutable object
and doing refactoring
...

最終的に達成するためのホイールを発明していることをかなり速く気づくでしょう abc.ABCMeta

abc.ABCMeta 不足しているインターフェース機能の便利な追加として提案されましたが、それはpythonのような言語で十分です。

確かに、バージョン3を記述し、新しい構文と不変のインターフェイスコンセプトを追加する間に、より良く拡張することができました...

結論:

The abc.ABCMeta IS "pythonic" interface in python
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.