Antworten:
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.
NotImplementedError("Class %s doesn't implement aMethod()" % (self.__class__.__name__))
ist informativer Fehlermeldung :)
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 collections
Modul verwenden:
import collections
class MySet(collections.Set):
pass
Wenn wir versuchen, es zu verwenden, erhalten wir eine, TypeError
weil 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
Wir können unsere eigene abstrakte Basisklasse erstellen, indem wir die Metaklasse auf relevante Methoden setzen abc.ABCMeta
und den abc.abstractmethod
Dekorator 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.
Wir haben gezeigt, dass beim Erstellen einer abstrakten Basisklasse Schnittstellen für benutzerdefinierte Objekte in Python definiert werden.
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).
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.
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.
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.
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.
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:
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