Ist es möglich, abstrakte Klassen zu erstellen?

Lesezeit: 9 Minuten

Wie kann ich eine Klasse oder Methode in Python abstrakt machen?

Ich habe versucht, neu zu definieren __new__() so:

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 das erbt von F so:

class G(F):
    pass

dann kann ich nicht instanziieren G auch nicht, da er seine Superklasse nennt __new__ Methode.

Gibt es eine bessere Möglichkeit, eine abstrakte Klasse zu definieren?

  • Ja, Sie können abstrakte Klassen in Python mit dem Modul abc (Abstract Base Classes) erstellen. Diese Seite hilft Ihnen dabei: http://docs.python.org/2/library/abc.html

    – ORION

    30. November 2012 um 13:32 Uhr

Benutzeravatar von alexvassel
alexvassel

Verwenden Sie die abc Modul zum Erstellen abstrakter Klassen. Verwenden Sie die abstractmethod decorator, um ein Methoden-Abstract und ein Klassen-Abstract auf eine von drei Arten zu deklarieren, 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 angeben ABCMeta. Die Angabe der Metaklasse hat in Python 3 und Python 2 eine unterschiedliche Syntax. Die drei Möglichkeiten werden unten gezeigt:

# Python 3.4+
from abc import ABC, abstractmethod
class Abstract(ABC):
    @abstractmethod
    def foo(self):
        pass
# Python 3.0+
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass
# Python 2
from abc import ABCMeta, abstractmethod
class Abstract:
    __metaclass__ = ABCMeta

    @abstractmethod
    def foo(self):
        pass

Unabhängig davon, wie Sie es verwenden, können Sie keine abstrakte Klasse mit abstrakten Methoden instanziieren, aber Sie können eine Unterklasse instanziieren, die konkrete Definitionen dieser Methoden bereitstellt:

>>> Abstract()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Abstract with abstract methods foo
>>> class StillAbstract(Abstract):
...     pass
... 
>>> StillAbstract()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class StillAbstract with abstract methods foo
>>> class Concrete(Abstract):
...     def foo(self):
...         print('Hello, World')
... 
>>> Concrete()
<__main__.Concrete object at 0x7fc935d28898>

  • Was macht die @abstractmethod? Wieso brauchst du es? Wenn die Klasse bereits als abstrakt eingerichtet ist, sollte der Compiler/Interpret nicht wissen, dass alle Methoden von der betreffenden abstrakten Klasse stammen?

    – Charly Parker

    17. März 2014 um 3:30 Uhr

  • @CharlieParker – Das @abstractmethod macht es so, dass die dekorierte Funktion muss überschrieben werden, 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.

    – Falscher Name

    18. April 2014 um 6:48 Uhr

  • @CharlieParker – Grundsätzlich können Sie eine Klasse so definieren, dass die Unterklasse muss Implementieren Sie eine bestimmte Suite von Methoden, um überhaupt zu instanziieren.

    – Falscher Name

    18. April 2014 um 6:49 Uhr

  • Gibt es eine Möglichkeit, Benutzern das Erstellen von Abstract() ohne @abstractmethod-Methoden zu verbieten?

    – Jo

    2. Dezember 2014 um 7:45 Uhr

  • Warum kann ich nicht nur verwenden @abstractmethod ohne ABC? Es fühlt sich unpythonisch an. Wenn ich abstractmethod schreibe, bedeutet das, dass dies bereits eine abstrakte Basisklasse ist. Oder übersehe ich etw?

    – Hans

    7. Dezember 2019 um 23:51 Uhr


Benutzeravatar von Tim Gilbert
Tim Gilbert

Die alte Schule (Vor-PEP 3119) Weg, dies zu tun, ist nur zu raise NotImplementedError in der abstrakten Klasse, wenn eine abstrakte Methode aufgerufen wird.

class Abstract(object):
    def foo(self):
        raise NotImplementedError('subclasses must override foo()!')

class Derived(Abstract):
    def foo(self):
        print 'Hooray!'

>>> d = Derived()
>>> d.foo()
Hooray!
>>> a = Abstract()
>>> a.foo()
Traceback (most recent call last): [...]

Dies hat nicht die gleichen netten Eigenschaften wie die Verwendung von abc Modul tut. Sie können immer noch die abstrakte Basisklasse selbst instanziieren, und Sie werden Ihren Fehler erst finden, wenn Sie die abstrakte Methode zur Laufzeit aufrufen.

Aber wenn Sie es mit einer kleinen Menge einfacher Klassen zu tun haben, vielleicht mit nur ein paar abstrakten Methoden, ist dieser Ansatz etwas einfacher, als zu versuchen, sich durch die Klassen zu kämpfen abc Dokumentation.

  • Schätzen Sie die Einfachheit und Effektivität dieses Ansatzes.

    – mohit6up

    29. Mai 2019 um 21:13 Uhr

  • Haha, ich habe das überall in meinem OMSCS-Kurs gesehen und hatte keine Ahnung, was es war 🙂

    – mlSchüler33

    5. Juni 2020 um 7:40 Uhr

  • Wirklich hilfreich ist es aber nicht. Weil Sie vielleicht ein allgemeines Verhalten im Inneren implementieren möchten Abstract#foo. Das direkte Aufrufen sollte verboten werden, aber es sollte immer noch möglich sein, es mit anzurufen super().

    – Eric Duminil

    7. Juli 2021 um 6:40 Uhr

Benutzeravatar von Irv Kalb
Irv Kalb

Hier ist ein sehr einfacher Weg, ohne sich mit dem ABC-Modul befassen zu müssen.

In dem __init__ Methode der Klasse, die Sie als abstrakte Klasse verwenden möchten, können Sie den “Typ” von self überprüfen. Wenn der Typ self die Basisklasse ist, versucht der Aufrufer, die Basisklasse zu instanziieren, also löst er eine Ausnahme aus. Hier ist ein einfaches Beispiel:

class Base():
    def __init__(self):
        if type(self) is Base:
            raise Exception('Base is an abstract class and cannot be instantiated directly')
        # Any initialization code
        print('In the __init__  method of the Base class')

class Sub(Base):
    def __init__(self):
        print('In the __init__ method of the Sub class before calling __init__ of the Base class')
        super().__init__()
        print('In the __init__ method of the Sub class after calling __init__ of the Base class')

subObj = Sub()
baseObj = Base()

Wenn es ausgeführt wird, erzeugt es:

In the __init__ method of the Sub class before calling __init__ of the Base class
In the __init__  method of the Base class
In the __init__ method of the Sub class after calling __init__ of the Base class
Traceback (most recent call last):
  File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 16, in <module>
    baseObj = Base()
  File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 4, in __init__
    raise Exception('Base is an abstract class and cannot be instantiated directly')
Exception: Base is an abstract class and cannot be instantiated directly

Dies zeigt, dass Sie eine Unterklasse instanziieren können, die von einer Basisklasse erbt, aber Sie können die Basisklasse nicht direkt instanziieren.

Benutzeravatar von grepit
Grusel

Die meisten vorherigen Antworten waren richtig, 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 zu implementieren ist. In den folgenden Eltern- und Babyklassen beispielsweise essen sie beide, aber die Implementierung wird für jeden unterschiedlich sein, da Babys und Eltern unterschiedliche Arten von Lebensmitteln essen und die Häufigkeit, mit der sie essen, unterschiedlich ist. Die Unterklassen der Eat-Methode überschreiben also AbstractClass.eat.

from abc import ABC, abstractmethod

class AbstractClass(ABC):

    def __init__(self, value):
        self.value = value
        super().__init__()

    @abstractmethod
    def eat(self):
        pass

class Parents(AbstractClass):
    def eat(self):
        return "eat solid food "+ str(self.value) + " times each day"

class Babies(AbstractClass):
    def eat(self):
        return "Milk only "+ str(self.value) + " times or more each day"

food = 3    
mom = Parents(food)
print("moms ----------")
print(mom.eat())

infant = Babies(food)
print("infants ----------")
print(infant.eat())

AUSGANG:

moms ----------
eat solid food 3 times each day
infants ----------
Milk only 3 times or more each day

Clebs Benutzeravatar
Cleb

Wie in den anderen Antworten erläutert, können Sie abstrakte Klassen in Python verwenden, indem Sie die verwenden abc Modul. Unten gebe ich ein aktuelles Beispiel mit abstract @classmethod, @property und @abstractmethod (unter Verwendung von Python 3.6+). 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.

Lassen Sie uns zuerst eine Basisklasse namens erstellen Base:

from abc import ABC, abstractmethod

class Base(ABC):

    @classmethod
    @abstractmethod
    def from_dict(cls, d):
        pass
    
    @property
    @abstractmethod
    def prop1(self):
        pass

    @property
    @abstractmethod
    def prop2(self):
        pass

    @prop2.setter
    @abstractmethod
    def prop2(self, val):
        pass

    @abstractmethod
    def do_stuff(self):
        pass

Unser Base Klasse wird immer eine haben from_dict classmethoda property prop1 (schreibgeschützt) und a property prop2 (die auch gesetzt werden kann) sowie eine aufgerufene Funktion do_stuff. Auf welcher Klasse auch immer nun gebaut wird Base müssen alle diese vier Methoden/Eigenschaften implementieren. Bitte beachten Sie, dass zwei Dekorateure erforderlich sind, damit eine Methode abstrakt ist – classmethod und abstrakt property.

Jetzt könnten wir eine Klasse erstellen A so was:

class A(Base):
    def __init__(self, name, val1, val2):
        self.name = name
        self.__val1 = val1
        self._val2 = val2

    @classmethod
    def from_dict(cls, d):
        name = d['name']
        val1 = d['val1']
        val2 = d['val2']

        return cls(name, val1, val2)

    @property
    def prop1(self):
        return self.__val1

    @property
    def prop2(self):
        return self._val2

    @prop2.setter
    def prop2(self, value):
        self._val2 = value

    def do_stuff(self):
        print('juhu!')

    def i_am_not_abstract(self):
        print('I can be customized')

Alle erforderlichen Methoden/Eigenschaften sind implementiert und wir können natürlich auch zusätzliche Funktionen hinzufügen, die nicht Teil von sind Base (hier: i_am_not_abstract).

Jetzt können wir tun:

a1 = A('dummy', 10, 'stuff')
a2 = A.from_dict({'name': 'from_d', 'val1': 20, 'val2': 'stuff'})

a1.prop1
# prints 10

a1.prop2
# prints 'stuff'

Wie gewünscht, können wir nicht einstellen prop1:

a.prop1 = 100

wird zurückkehren

AttributeError: Attribut kann nicht gesetzt werden

Auch unsere from_dict Methode funktioniert gut:

a2.prop1
# prints 20

Definieren wir nun eine zweite Klasse B so was:

class B(Base):
    def __init__(self, name):
        self.name = name

    @property
    def prop1(self):
        return self.name

und versucht, ein Objekt wie folgt zu instanziieren:

b = B('iwillfail')

Wir erhalten einen Fehler

TypeError: Die abstrakte Klasse B kann nicht mit den abstrakten Methoden do_stuff, from_dict, prop2 instanziiert werden

Auflistung aller Dinge, die in definiert sind Base die wir nicht implementiert haben B.

  • Beachten Sie, dass es eine Annotation @abstactclassmethod gibt, die Sie anstelle von zwei separaten verwenden können

    – Fischen ist Leben

    23. Februar um 8:40 Uhr

  • @FishingIsLife: Laut Dokumentation @abstractclassmethod ist ab Version 3.3 veraltet, wenn ich es richtig verstehe.

    – Cleb

    1. März um 8:57

  • Sie haben Recht. Danke für den Tipp.

    – Fischen ist Leben

    1. März um 9:14

Benutzeravatar von Pipo
Pipo

Dieser wird in Python 3 funktionieren

from abc import ABCMeta, abstractmethod

class Abstract(metaclass=ABCMeta):

    @abstractmethod
    def foo(self):
        pass

Abstract()
>>> TypeError: Can not instantiate abstract class Abstract with abstract methods foo

  • Beachten Sie, dass es eine Annotation @abstactclassmethod gibt, die Sie anstelle von zwei separaten verwenden können

    – Fischen ist Leben

    23. Februar um 8:40 Uhr

  • @FishingIsLife: Laut Dokumentation @abstractclassmethod ist ab Version 3.3 veraltet, wenn ich es richtig verstehe.

    – Cleb

    1. März um 8:57

  • Sie haben Recht. Danke für den Tipp.

    – Fischen ist Leben

    1. März um 9:14

Benutzeravatar von Giovanni Angeli
Giovanni Angeli

auch das funktioniert und ist einfach:

class A_abstract(object):

    def __init__(self):
        # quite simple, old-school way.
        if self.__class__.__name__ == "A_abstract": 
            raise NotImplementedError("You can't instantiate this abstract class. Derive it, please.")

class B(A_abstract):

        pass

b = B()

# here an exception is raised:
a = A_abstract()

1436490cookie-checkIst es möglich, abstrakte Klassen zu erstellen?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy