Wie kann ich eine Klasse oder Methode in Python abstrakt machen?
Ich habe versucht, __new__()
so neu zu definieren:
class F:
def __new__(cls):
raise Exception("Unable to create an instance of abstract class %s" %cls)
aber jetzt, wenn ich eine Klasse erstelle G
, die von F
so erbt :
class G(F):
pass
dann kann ich auch nicht instanziieren G
, da es die __new__
Methode seiner Superklasse aufruft .
Gibt es eine bessere Möglichkeit, eine abstrakte Klasse zu definieren?
python
class
inheritance
abstract-class
abstract
Martin Thoma
quelle
quelle
Antworten:
Verwenden Sie das
abc
Modul, um abstrakte Klassen zu erstellen. Verwenden Sie denabstractmethod
Decorator, um eine Methodenzusammenfassung zu deklarieren, und deklarieren Sie eine Klassenzusammenfassung auf drei Arten, abhängig von Ihrer Python-Version.In Python 3.4 und höher können Sie von erben
ABC
. In früheren Versionen von Python müssen Sie die Metaklasse Ihrer Klasse als angebenABCMeta
. Die Angabe der Metaklasse hat in Python 3 und Python 2 unterschiedliche Syntax. Die drei Möglichkeiten sind nachstehend aufgeführt:Unabhängig davon, wie Sie es verwenden, können Sie keine abstrakte Klasse mit abstrakten Methoden instanziieren, sondern eine Unterklasse, die konkrete Definitionen dieser Methoden enthält:
quelle
@abstractmethod
macht es so, dass die dekorierte Funktion überschrieben werden muss , bevor die Klasse instanziiert werden kann. Aus den Dokumenten:A class that has a metaclass derived from ABCMeta cannot be instantiated unless all of its abstract methods and properties are overridden.
@abstractmethod
für__init__
Verfahren und finden stackoverflow.com/q/44800659/547270Die Methode der alten Schule (vor PEP 3119 ) besteht darin, nur
raise NotImplementedError
in der abstrakten Klasse zu arbeiten, wenn eine abstrakte Methode aufgerufen wird.Dies hat nicht die gleichen schönen Eigenschaften wie die Verwendung des
abc
Moduls. Sie können die abstrakte Basisklasse selbst weiterhin instanziieren, und Sie werden Ihren Fehler erst finden, wenn Sie die abstrakte Methode zur Laufzeit aufrufen.Wenn Sie sich jedoch mit einer kleinen Anzahl einfacher Klassen beschäftigen, möglicherweise mit nur wenigen abstrakten Methoden, ist dieser Ansatz etwas einfacher als der Versuch, die
abc
Dokumentation durchzuarbeiten .quelle
Hier ist ein sehr einfacher Weg, ohne sich mit dem ABC-Modul befassen zu müssen.
In der
__init__
Methode der Klasse, die Sie eine abstrakte Klasse sein möchten, können Sie den "Typ" von self überprüfen. Wenn der Typ von self die Basisklasse ist, versucht der Aufrufer, die Basisklasse zu instanziieren, also eine Ausnahme auslösen. Hier ist ein einfaches Beispiel:Beim Ausführen wird Folgendes erzeugt:
Dies zeigt, dass Sie eine Unterklasse instanziieren können, die von einer Basisklasse erbt, die Basisklasse jedoch nicht direkt instanziieren können.
quelle
Die meisten vorherigen Antworten waren korrekt, aber hier ist die Antwort und das Beispiel für Python 3.7. Ja, Sie können eine abstrakte Klasse und Methode erstellen. Nur zur Erinnerung, manchmal sollte eine Klasse eine Methode definieren, die logisch zu einer Klasse gehört, aber diese Klasse kann nicht angeben, wie die Methode implementiert werden soll. In den folgenden Klassen für Eltern und Babys essen sie beide, aber die Implementierung ist für jede Klasse unterschiedlich, da Babys und Eltern eine andere Art von Essen essen und die Häufigkeit, mit der sie essen, unterschiedlich ist. Die Unterklassen der eat-Methode überschreiben also AbstractClass.eat.
AUSGABE:
quelle
import abc
ist nutzlos.Dieser wird in Python 3 funktionieren
quelle
Wie in den anderen Antworten erläutert, können Sie mit dem
abc
Modul abstrakte Klassen in Python verwenden . Im Folgenden gebe ich ein tatsächliches Beispiel mit abstraktem@classmethod
,@property
und@abstractmethod
(mit Python 3.6 oder höher). Für mich ist es normalerweise einfacher, mit Beispielen zu beginnen, die ich einfach kopieren und einfügen kann. Ich hoffe, diese Antwort ist auch für andere nützlich.Erstellen wir zunächst eine Basisklasse mit dem Namen
Base
:Unsere
Base
Klasse wird immer einfrom_dict
classmethod
, einproperty
prop1
(schreibgeschützt) und einproperty
prop2
(das auch gesetzt werden kann) sowie eine aufgerufene Funktion habendo_stuff
. Unabhängig davon, auf welcher Klasse jetzt basiertBase
, muss all dies für Methoden / Eigenschaften implementiert werden. Bitte beachten Sie, dass für eine abstrakte Methode zwei Dekorateure erforderlich sind -classmethod
und eine abstrakteproperty
.Jetzt könnten wir eine Klasse
A
wie diese erstellen :Alle erforderlichen Methoden / Eigenschaften sind implementiert und wir können natürlich auch zusätzliche Funktionen hinzufügen, die nicht Teil von
Base
(hier :) sindi_am_not_abstract
.Jetzt können wir tun:
Wie gewünscht können wir nicht einstellen
prop1
:wird zurückkehren
Auch unsere
from_dict
Methode funktioniert gut:Wenn wir jetzt eine zweite Klasse
B
wie diese definieren:und versuchte, ein Objekt wie folgt zu instanziieren:
Wir werden einen Fehler bekommen
Auflisten aller definierten Dinge, in
Base
denen wir nicht implementiert habenB
.quelle
auch das funktioniert und ist einfach:
quelle
Sie können die __new__- Methode auch zu Ihrem Vorteil nutzen. Du hast gerade etwas vergessen. Die Methode __new__ gibt immer das neue Objekt zurück, daher müssen Sie die neue Methode der Oberklasse zurückgeben. Gehen Sie wie folgt vor.
Wenn Sie die neue Methode verwenden, müssen Sie das Objekt zurückgeben, nicht das Schlüsselwort None. Das ist alles was du verpasst hast.
quelle
Ich finde die akzeptierte Antwort und alle anderen seltsam, da sie
self
zu einer abstrakten Klasse übergehen . Eine abstrakte Klasse wird nicht instanziiert, kann also keine habenself
.Probieren Sie es aus, es funktioniert.
quelle
quelle
In Ihrem Code-Snippet können Sie dies auch beheben, indem Sie eine Implementierung für die
__new__
Methode in der Unterklasse bereitstellen :Aber das ist ein Hack und ich rate Ihnen davon ab, es sei denn, Sie wissen, was Sie tun. In fast allen Fällen empfehle ich Ihnen, das
abc
Modul zu verwenden, das andere vor mir vorgeschlagen haben.Wenn Sie eine neue (Basis-) Klasse erstellen, machen Sie sie
object
wie folgt zu einer Unterklasse :class MyBaseClass(object):
. Ich weiß nicht mehr, ob es so wichtig ist, aber es hilft dabei, die Stilkonsistenz Ihres Codes beizubehaltenquelle
Nur eine kurze Ergänzung zu @ TimGilberts Antwort der alten Schule ... Sie können die init () -Methode Ihrer abstrakten Basisklasse dazu bringen , eine Ausnahme auszulösen, und das würde verhindern, dass sie instanziiert wird, nein?
quelle