Was ist der Unterschied zwischen abstrakter Klasse und Schnittstelle in Python?
Was Sie manchmal sehen werden, ist das Folgende:
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), existiert die Unterscheidung zwischen Abstraktion und Schnittstelle im Java-Stil nicht. Wenn sich jemand bemüht, eine formale Schnittstelle zu definieren, handelt es sich auch um eine abstrakte Klasse. Die einzigen Unterschiede bestünden in der angegebenen Absicht in der Doku-Zeichenfolge.
Und der Unterschied zwischen abstrakt und Benutzeroberfläche ist eine haarsträubende Sache, wenn Sie Enten tippen.
Java verwendet Schnittstellen, weil es keine Mehrfachvererbung hat.
Da Python mehrfach vererbt wird, 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
Hierbei wird eine Art abstrakte Superklasse mit Mixins verwendet, um konkrete, nicht zusammenhängende Unterklassen zu erstellen.
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.
Angenommen, wir möchten eine der abstrakten Basisklassen aus dem Modul collections
verwenden:
import collections
class MySet(collections.Set):
pass
Wenn wir versuchen, es zu verwenden, erhalten wir ein TypeError
, da 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__
Wir müssen also mindestens __contains__
, __iter__
und __len__
implementieren. 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
Wir können unsere eigene abstrakte Basisklasse erstellen, indem wir die Metaklasse auf abc.ABCMeta
setzen und den Dekorator abc.abstractmethod
für relevante Methoden verwenden. Die Metaklasse fügt die dekorierten Funktionen zum Attribut __abstractmethods__
hinzu und verhindert die Instanziierung, bis diese definiert sind.
import abc
Beispielsweise wird "effektiv" als etwas definiert, das in Worten ausgedrückt werden kann. Angenommen, wir wollten eine abstrakte Basisklasse definieren, die effektiv ist, in Python 2:
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 versuche 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, gehen Sie wie folgt vor:
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 vorgestellten Methoden müssten diese Methode jedoch mit dem Modul abc
anpassen.
Wir haben gezeigt, dass die Erstellung einer abstrakten Basisklasse Schnittstellen für benutzerdefinierte Objekte in Python definiert.
Python> = 2.6 hat abstrakte Basisklassen .
Abstrakte Basisklassen (abgekürzt ABCs) ergänzen das Duck-Typing, 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 Modul "Collections"), Zahlen (im Modul "Numbers") und Streams (im Modul "Io"). 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 kenne mich damit nicht wirklich aus, aber es gibt eine Wiki-Seite hier die vielleicht hilft.
Im Allgemeinen benötigen Sie das Konzept abstrakter Klassen oder Interfaces in python nicht (bearbeitet - Einzelheiten finden Sie in der Antwort von S.Lott).
Python hat keines der beiden Konzepte.
Es benutzt Duck Typing, wodurch Schnittstellen überflüssig wurden (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 do exist ( http://docs.python.org/library/abc.html ). Außerdem können Sie Methoden angeben, die in Unterklassen implementiert werden müssen. Die Syntax gefällt mir nicht besonders, aber die Funktion ist vorhanden. In den meisten Fällen ist es wahrscheinlich besser, auf der Client-Seite "using" die Enten-Eingabe zu verwenden.
Einfacher ausgedrückt: 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 unbedingt leer.
Warum unterscheiden: In Python gibt es keinen großen praktischen Unterschied, aber auf der Planungsebene für ein großes Projekt könnte es üblicher sein, über Schnittstellen zu sprechen, da es keinen Code gibt. Vor allem, wenn Sie mit Java Programmierern arbeiten, die an den Begriff gewöhnt sind.
Im Allgemeinen werden Schnittstellen nur in Sprachen verwendet, die das Einfachvererbungsklassenmodell verwenden. In diesen Sprachen mit einfacher Vererbung werden Schnittstellen normalerweise verwendet, wenn eine Klasse eine bestimmte Methode oder einen Methodensatz verwenden kann. Auch in diesen Sprachen mit einfacher Vererbung werden abstrakte Klassen verwendet, um entweder zusätzlich zu keiner oder mehreren Methoden Klassenvariablen zu definieren oder um das Modell mit einfacher Vererbung zu nutzen, um den Klassenbereich zu begrenzen, der eine Reihe von Methoden verwenden kann.
Sprachen, die das Mehrfachvererbungsmodell unterstützen, verwenden in der Regel 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.
Abstrakte Klassen sind Klassen, die eine oder mehrere abstrakte Methoden enthalten. Abstrakte Klassen können neben abstrakten Methoden auch statische Methoden, Klassen- und Instanzmethoden haben. Aber im Falle einer Schnittstelle wird es nur abstrakte Methoden geben, keine anderen. Daher ist es nicht obligatorisch, abstrakte Klassen zu erben, aber es ist obligatorisch, Schnittstellen zu erben.