Unterschied zwischen abstrakter Klasse und Schnittstelle in Python

557

Was ist der Unterschied zwischen abstrakter Klasse und Schnittstelle in Python?

Gizmo
quelle

Antworten:

618

Was Sie manchmal sehen werden, ist Folgendes:

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" )

Da Python keinen formalen Schnittstellenvertrag hat (und nicht benötigt), gibt es keine Unterscheidung zwischen Abstraktion und Schnittstelle im Java-Stil. Wenn jemand versucht, eine formale Schnittstelle zu definieren, handelt es sich auch um eine abstrakte Klasse. Die einzigen Unterschiede wären in der angegebenen Absicht in der Dokumentzeichenfolge.

Und der Unterschied zwischen abstrakt und Schnittstelle ist eine haarsträubende Sache, wenn Sie Enten tippen.

Java verwendet Schnittstellen, da es keine Mehrfachvererbung hat.

Da Python mehrfach vererbt ist, wird möglicherweise auch so etwas angezeigt

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

Dies verwendet eine Art abstrakte Superklasse mit Mixins, um konkrete Unterklassen zu erstellen, die nicht zusammenhängend sind.

S.Lott
quelle
5
S. Lott, meinen Sie damit, dass die Unterscheidung zwischen has-a (Schnittstelle) und is-a (Vererbung) aufgrund der Ententypisierung nicht wesentlich ist?
Lorenzo
3
Der Unterschied zwischen abstrakt und Schnittstelle ist eine haarsträubende Sache, wenn Sie Enten tippen. Ich weiß nicht, was "substanziell" bedeutet. Es ist "echt" - es hat Substanz - aus gestalterischer Sicht. Aus sprachlicher Sicht gibt es jedoch möglicherweise keine Unterstützung. Sie können Konventionen anwenden, um zwischen einer abstrakten Klasse und einer Schnittstellenklassendefinition in Python zu unterscheiden.
S.Lott
26
@ L.DeLeo - bist du sicher, dass deine Vorstellung von has-a vs. is-a richtig ist? Ich betrachte die Unterscheidung im Allgemeinen als has-a = Mitgliedsvariable vs. is-a = Vererbung (übergeordnete Klasse oder Schnittstelle). Denken Sie vergleichbar oder Liste in Java; Dies sind is-a-Beziehungen, unabhängig davon, ob es sich um Schnittstellen oder abstrakte Klassen handelt.
dimo414
43
NotImplementedError("Class %s doesn't implement aMethod()" % (self.__class__.__name__))ist informativer Fehlermeldung :)
naught101
9
@Lorenzo a has-a-Beziehung hat nichts mit Vererbung, Ententypisierung, Schnittstellen und abstrakten Klassen zu tun (alle vier beziehen sich auf is-a-Beziehungen).
Karl Richter
197

Was ist der Unterschied zwischen abstrakter Klasse und Schnittstelle in Python?

Eine Schnittstelle für ein Objekt besteht aus einer Reihe von Methoden und Attributen für dieses Objekt.

In Python können wir eine abstrakte Basisklasse verwenden, um eine Schnittstelle zu definieren und zu erzwingen.

Verwenden einer abstrakten Basisklasse

Angenommen, wir möchten eine der abstrakten Basisklassen aus dem collectionsModul verwenden:

import collections
class MySet(collections.Set):
    pass

Wenn wir versuchen, es zu verwenden, erhalten wir eine, TypeErrorweil die von uns erstellte Klasse das erwartete Verhalten von Mengen nicht unterstützt:

>>> 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__

Also wir sind bei implementieren dest __contains__ , __iter__und __len__. Verwenden wir dieses Implementierungsbeispiel aus der Dokumentation :

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

Implementierung: Erstellen einer abstrakten Basisklasse

Wir können unsere eigene abstrakte Basisklasse erstellen, indem wir die Metaklasse auf relevante Methoden setzen abc.ABCMetaund den abc.abstractmethodDekorator verwenden. Die Metaklasse fügt dem __abstractmethods__Attribut die dekorierten Funktionen hinzu und verhindert so die Instanziierung, bis diese definiert sind.

import abc

Zum Beispiel wird "effizient" als etwas definiert, das in Worten ausgedrückt werden kann. Angenommen, wir wollten in Python 2 eine abstrakte Basisklasse definieren, die effektiv ist:

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

Oder in Python 3 mit der geringfügigen Änderung der Metaklassendeklaration:

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

Wenn wir nun versuchen, ein effektives Objekt zu erstellen, ohne die Schnittstelle zu implementieren:

class MyEffable(Effable): 
    pass

und versuchen Sie es zu instanziieren:

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

Uns wird gesagt, dass wir den Job noch nicht beendet haben.

Wenn wir nun die erwartete Schnittstelle bereitstellen:

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

Wir können dann die konkrete Version der Klasse verwenden, die von der abstrakten abgeleitet ist:

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

Es gibt andere Dinge, die wir damit tun könnten, wie das Registrieren virtueller Unterklassen, die diese Schnittstellen bereits implementieren, aber ich denke, das würde den Rahmen dieser Frage sprengen. Die anderen hier gezeigten Methoden müssten diese Methode mit dem anpassenabc Moduls zu tun.

Fazit

Wir haben gezeigt, dass beim Erstellen einer abstrakten Basisklasse Schnittstellen für benutzerdefinierte Objekte in Python definiert werden.

Aaron Hall
quelle
101

Python> = 2.6 hat abstrakte Basisklassen .

Abstrakte Basisklassen (abgekürzte ABCs) ergänzen die Ententypisierung, indem sie eine Möglichkeit bieten, Schnittstellen zu definieren, wenn andere Techniken wie hasattr () ungeschickt wären. Python enthält viele integrierte ABCs für Datenstrukturen (im Sammlungsmodul), Zahlen (im Zahlenmodul) und Streams (im Io-Modul). Mit dem abc-Modul können Sie Ihr eigenes ABC erstellen.

Es gibt auch das Zope Interface- Modul, das von Projekten außerhalb von zope wie Twisted verwendet wird. Ich bin nicht wirklich vertraut mit ihm, aber es gibt eine Wiki - Seite hier Das könnte helfen.

Im Allgemeinen benötigen Sie kein Konzept für abstrakte Klassen oder Schnittstellen in Python (bearbeitet - Einzelheiten finden Sie in der Antwort von S.Lott).

JimB
quelle
2
Was gewinnen Sie durch die Verwendung von ABCs in Python?
CpILL
38

Python hat keines der beiden Konzepte.

Es verwendet Enten-Typisierung, wodurch keine Schnittstellen mehr benötigt werden (zumindest für den Computer :-))

Python <= 2.5: Basisklassen existieren offensichtlich, aber es gibt keine explizite Möglichkeit, eine Methode als "rein virtuell" zu markieren, sodass die Klasse nicht wirklich abstrakt ist.

Python> = 2.6: Abstrakte Basisklassen tun exist ( http://docs.python.org/library/abc.html ). Außerdem können Sie Methoden angeben, die in Unterklassen implementiert werden müssen. Ich mag die Syntax nicht sehr, aber die Funktion ist da. Meistens ist es wahrscheinlich besser, die Ententypisierung von der Client-Seite "using" aus zu verwenden.

Douglas Leeder
quelle
3
Python 3.0 fügt echte abstrakte Basisklassen hinzu. Sie werden im Sammlungsmodul sowie an anderen Stellen verwendet. docs.python.org/3.0/library/abc.html
Lara Dougan
Eine Referenz darüber, warum das Eingeben von Enten die Notwendigkeit von Schnittstellen beseitigt, wäre hilfreich. Es scheint mir nicht offensichtlich, dass das Eingeben von Enten, das ich als die Fähigkeit verstehe, eine Methode oder ein Attribut auf ein Objekt zu "stoßen", bedeuten würde, dass Sie nicht das erforderliche Verhalten angeben müssen (und den Compiler dazu bringen müssen, Sie daran zu erinnern) um sie zu implementieren), so verstehe ich abstrakte Basisklassen.
Reb.Cabin
Es ist weniger die Ententypisierung als die Unterstützung für Mehrfachvererbung, die die künstliche Grenze zwischen Schnittstelle und abstrakter Klasse entfernt, die z. B. Java gezeichnet hat.
Masi
35

Einfacher zu erklären: Eine Benutzeroberfläche ähnelt einer leeren Muffinform. Es ist eine Klassendatei mit einer Reihe von Methodendefinitionen, die keinen Code enthalten.

Eine abstrakte Klasse ist dasselbe, aber nicht alle Funktionen müssen leer sein. Einige können Code haben. Es ist nicht streng leer.

Warum differenzieren: Es gibt keinen großen praktischen Unterschied in Python, aber auf der Planungsebene für ein großes Projekt könnte es üblicher sein, über Schnittstellen zu sprechen, da es keinen Code gibt. Besonders wenn Sie mit Java-Programmierern arbeiten, die an den Begriff gewöhnt sind.

SilentSteel
quelle
+1 für die Unterscheidung, wo ABC eigene Implementierungen haben kann - was eine sehr coole Art zu sein scheint, sich selbst zu überlisten
Titou
17

Im Allgemeinen werden Schnittstellen nur in Sprachen verwendet, die das Klassenmodell der Einzelvererbung verwenden. In diesen Sprachen mit einfacher Vererbung werden normalerweise Schnittstellen verwendet, wenn eine Klasse eine bestimmte Methode oder einen bestimmten Satz von Methoden verwenden könnte. Auch in diesen Einzelvererbungssprachen werden abstrakte Klassen verwendet, um entweder Klassenvariablen zusätzlich zu keiner oder mehreren Methoden zu definieren oder um das Einzelvererbungsmodell zu nutzen, um den Bereich von Klassen zu begrenzen, die eine Reihe von Methoden verwenden könnten.

Sprachen, die das Mehrfachvererbungsmodell unterstützen, verwenden normalerweise nur Klassen oder abstrakte Basisklassen und keine Schnittstellen. Da Python Mehrfachvererbung unterstützt, werden keine Schnittstellen verwendet, und Sie möchten Basisklassen oder abstrakte Basisklassen verwenden.

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

Lara Dougan
quelle
2

Abstrakte Klassen sind Klassen, die eine oder mehrere abstrakte Methoden enthalten. Abstrakte Klassen können neben abstrakten Methoden auch statische, Klassen- und Instanzmethoden haben. Im Falle einer Schnittstelle gibt es jedoch nur abstrakte Methoden, keine anderen. Daher ist es nicht obligatorisch, eine abstrakte Klasse zu erben, aber es ist obligatorisch, eine Schnittstelle zu erben.

Akash Ukarande
quelle
1

Der Vollständigkeit halber sollten wir PEP3119 erwähnen, in dem ABC eingeführt und mit Schnittstellen verglichen wurde, sowie den ursprünglichen Kommentar von Talin .

Die abstrakte Klasse ist keine perfekte Schnittstelle:

  • gehört zur Vererbungshierarchie
  • ist veränderlich

Aber wenn Sie darüber nachdenken, es auf Ihre eigene Weise zu schreiben:

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
...

Sie werden schnell feststellen, dass Sie das Rad erfinden, um es irgendwann zu erreichen abc.ABCMeta

abc.ABCMeta wurde als nützliche Ergänzung der fehlenden Schnittstellenfunktionalität vorgeschlagen, und das ist in einer Sprache wie Python fair genug.

Sicherlich konnte es beim Schreiben von Version 3 und beim Hinzufügen einer neuen Syntax und eines unveränderlichen Schnittstellenkonzepts besser verbessert werden ...

Fazit:

The abc.ABCMeta IS "pythonic" interface in python
Sławomir Lenart
quelle