640 Stimmen

Unterschied zwischen abstrakter Klasse und Schnittstelle in Python

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

686voto

S.Lott Punkte 371691

Manchmal werden Sie folgendes sehen:

class Abstract1:
    """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 auch nicht braucht), gibt es die Java-ähnliche Unterscheidung zwischen Abstraktion und Schnittstelle nicht. Wenn sich jemand die Mühe macht, eine formale Schnittstelle zu definieren, wird sie auch eine abstrakte Klasse sein. Der einzige Unterschied wäre die erklärte Absicht im Docstring.

Und der Unterschied zwischen abstrakt und Schnittstelle ist eine haarspalterische Sache, wenn man Enten tippen muss.

Java verwendet Schnittstellen, weil es keine Mehrfachvererbung gibt.

Da Python über Mehrfachvererbung verfügt, können Sie auch etwas wie dieses sehen

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

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

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

class Concrete1(SomeAbstraction, Mixin1):
    pass

class Concrete2(SomeAbstraction, Mixin2):
    pass

Dabei wird eine Art abstrakte Oberklasse mit Mixins verwendet, um konkrete Unterklassen zu erstellen, die disjunkt sind.

229voto

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

Eine Schnittstelle für ein Objekt ist ein Satz von Methoden und Attributen für dieses Objekt.

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

Verwendung einer abstrakten Basisklasse

Nehmen wir an, wir wollen eine der abstrakten Basisklassen aus der collections Modul:

import collections
class MySet(collections.Set):
    pass

Wenn wir versuchen, es zu benutzen, 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__

Wir sind also verpflichtet, bei am wenigsten __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

Umsetzung: Erstellen einer abstrakten Basisklasse

Wir können unsere eigene abstrakte Basisklasse erstellen, indem wir die Metaklasse auf abc.ABCMeta und die Verwendung des abc.abstractmethod Dekors für die entsprechenden Methoden. Die Metaklasse fügt die dekorierten Funktionen in die __abstractmethods__ Attribut und verhindert die Instanziierung, bis diese definiert sind.

import abc

Ein Beispiel: "effable" ist definiert als etwas, das in Worten ausgedrückt werden kann. Nehmen wir an, wir wollten in Python 2 eine abstrakte Basisklasse definieren, die "effable" 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 leichten Ä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 funktionierendes Objekt zu erstellen, ohne die Schnittstelle zu implementieren:

class MyEffable(Effable): 
    pass

und versuchen, 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 die Arbeit nicht beendet haben.

Wenn wir nun die erwartete Schnittstelle bereitstellen:

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

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

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

Es gibt noch andere Dinge, die wir damit tun könnten, z. B. virtuelle Unterklassen registrieren, die diese Schnittstellen bereits implementieren, aber ich denke, das würde den Rahmen dieser Frage sprengen. Die anderen Methoden, die hier gezeigt werden, müssten diese Methode mit Hilfe der abc Modul, dies zu tun.

Schlussfolgerung

Wir haben gezeigt, dass die Erstellung einer abstrakten Basisklasse Schnittstellen für benutzerdefinierte Objekte in Python definiert.

102voto

JimB Punkte 95188

Python >= 2.6 hat Abstrakte Basisklassen .

Abstrakte Basisklassen (abgekürzt ABCs) ergänzen die Duck-Typisierung durch eine Möglichkeit bieten, Schnittstellen zu definieren wenn andere Techniken wie hasattr() ungeschickt wären. Python kommt mit viele eingebaute ABCs für Datenstrukturen (im Modul collections), Zahlen (im Modul numbers), und Streams (im io-Modul). Sie können Ihr eigenes ABC mit dem abc-Modul erstellen.

Außerdem gibt es die Zope-Schnittstelle Modul, das von Projekten außerhalb von Zope, wie Twisted, verwendet wird. Ich bin nicht wirklich damit vertraut, aber es gibt eine Wikiseite ici die helfen könnten.

Im Allgemeinen brauchen Sie das Konzept der abstrakten Klassen oder Schnittstellen in Python nicht (bearbeitet - siehe die Antwort von S. Lott für Details).

51voto

SilentSteel Punkte 2169

Um es einfacher zu erklären: Eine Schnittstelle ist so etwas wie ein leeres Muffinblech. Es handelt sich um eine Klassendatei mit einer Reihe von Methodendefinitionen, die keinen Code enthalten.

Eine abstrakte Klasse ist das Gleiche, aber nicht alle Funktionen müssen leer sein. Einige können Code enthalten. Sie ist nicht unbedingt leer.

Warum differenzieren: 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 zusammenarbeiten, die an den Begriff gewöhnt sind.

41voto

Douglas Leeder Punkte 50423

In Python gibt es keines der beiden Konzepte wirklich.

Sie verwendet Duckmäuserschreiben wodurch die Notwendigkeit von Schnittstellen entfällt (zumindest für den Computer :-))

Python <= 2.5: Es gibt zwar Basisklassen, aber es gibt keine explizite Möglichkeit, eine Methode als "rein virtuell" zu kennzeichnen, so dass die Klasse nicht wirklich abstrakt ist.

Python >= 2.6: Abstrakte Basisklassen tun existieren ( http://docs.python.org/library/abc.html ). Und erlauben Sie die Angabe von Methoden, 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, die Duck-Typisierung auf der "verwendenden" Client-Seite zu verwenden.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X