abc — Abstrakte Basisklassen

Quellcode: Lib/abc.py


Dieses Modul stellt die Infrastruktur zur Definition von abstrakten Basisklassen (ABCs) in Python bereit, wie in PEP 3119 beschrieben; siehe die PEP für die Gründe, warum dies zu Python hinzugefügt wurde. (Siehe auch PEP 3141 und das Modul numbers bezüglich einer Typenhierarchie für Zahlen basierend auf ABCs.)

Das Modul collections enthält einige konkrete Klassen, die von ABCs abgeleitet sind; diese können natürlich weiter abgeleitet werden. Darüber hinaus enthält das Untermodul collections.abc einige ABCs, die verwendet werden können, um zu testen, ob eine Klasse oder Instanz eine bestimmte Schnittstelle bereitstellt, z. B. ob sie hashable ist oder ob sie eine mapping ist.

Dieses Modul stellt die Metaklasse ABCMeta zur Definition von ABCs und eine Hilfsklasse ABC zur alternativen Definition von ABCs durch Vererbung bereit

class abc.ABC

Eine Hilfsklasse, die ABCMeta als ihre Metaklasse hat. Mit dieser Klasse kann eine abstrakte Basisklasse einfach durch Ableiten von ABC erstellt werden, wodurch die manchmal verwirrende Verwendung von Metaklassen vermieden wird, zum Beispiel

from abc import ABC

class MyABC(ABC):
    pass

Beachten Sie, dass der Typ von ABC immer noch ABCMeta ist. Daher erfordert die Vererbung von ABC die üblichen Vorsichtsmaßnahmen bezüglich der Verwendung von Metaklassen, da Mehrfachvererbung zu Metaklassenkonflikten führen kann. Man kann auch eine abstrakte Basisklasse definieren, indem man das Schlüsselwort `metaclass` übergibt und ABCMeta direkt verwendet, zum Beispiel

from abc import ABCMeta

class MyABC(metaclass=ABCMeta):
    pass

Hinzugefügt in Version 3.4.

class abc.ABCMeta

Metaklasse zur Definition von Abstrakten Basisklassen (ABCs).

Verwenden Sie diese Metaklasse, um eine ABC zu erstellen. Eine ABC kann direkt unterklassifiziert werden und fungiert dann als Mix-in-Klasse. Sie können auch nicht verwandte konkrete Klassen (sogar eingebaute Klassen) und nicht verwandte ABCs als "virtuelle Unterklassen" registrieren – diese und ihre Nachkommen werden von der integrierten Funktion issubclass() als Unterklassen der registrierenden ABC betrachtet, aber die registrierende ABC wird nicht in ihrer MRO (Method Resolution Order) angezeigt, noch werden von der registrierenden ABC definierte Methodenimplementierungen aufrufbar sein (nicht einmal über super()). [1]

Klassen, die mit einer Metaklasse von ABCMeta erstellt wurden, haben die folgende Methode

register(subclass)

Registriert subclass als "virtuelle Unterklasse" dieser ABC. Zum Beispiel

from abc import ABC

class MyABC(ABC):
    pass

MyABC.register(tuple)

assert issubclass(tuple, MyABC)
assert isinstance((), MyABC)

Geändert in Version 3.3: Gibt die registrierte Unterklasse zurück, um sie als Klassen-Decorator verwenden zu können.

Geändert in Version 3.4: Um Aufrufe von register() zu erkennen, können Sie die Funktion get_cache_token() verwenden.

Sie können diese Methode auch in einer abstrakten Basisklasse überschreiben

__subclasshook__(subclass)

(Muss als Klassenmethode definiert werden.)

Prüft, ob subclass als Unterklasse dieser ABC betrachtet wird. Das bedeutet, dass Sie das Verhalten von issubclass() weiter anpassen können, ohne register() für jede Klasse aufrufen zu müssen, die Sie als Unterklasse der ABC betrachten möchten. (Diese Klassenmethode wird von der Methode __subclasscheck__() der ABC aufgerufen.)

Diese Methode sollte True, False oder NotImplemented zurückgeben. Wenn sie True zurückgibt, wird subclass als Unterklasse dieser ABC betrachtet. Wenn sie False zurückgibt, wird subclass nicht als Unterklasse dieser ABC betrachtet, auch wenn sie es normalerweise wäre. Wenn sie NotImplemented zurückgibt, wird die Unterklassenprüfung mit dem üblichen Mechanismus fortgesetzt.

Für eine Demonstration dieser Konzepte siehe dieses Beispiel für eine ABC-Definition

class Foo:
    def __getitem__(self, index):
        ...
    def __len__(self):
        ...
    def get_iterator(self):
        return iter(self)

class MyIterable(ABC):

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    def get_iterator(self):
        return self.__iter__()

    @classmethod
    def __subclasshook__(cls, C):
        if cls is MyIterable:
            if any("__iter__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

MyIterable.register(Foo)

Die ABC MyIterable definiert die Standard-Iterable-Methode, __iter__(), als abstrakte Methode. Die hier gegebene Implementierung kann immer noch von Unterklassen aufgerufen werden. Die Methode get_iterator() ist ebenfalls Teil der abstrakten Basisklasse MyIterable, muss aber in nicht-abstrakten abgeleiteten Klassen nicht überschrieben werden.

Die Klassenmethode __subclasshook__(), die hier definiert ist, besagt, dass jede Klasse, die eine Methode __iter__() in ihrem __dict__ hat (oder in dem einer ihrer Basisklassen, zugänglich über die Liste __mro__), ebenfalls als MyIterable betrachtet wird.

Schließlich macht die letzte Zeile Foo zu einer virtuellen Unterklasse von MyIterable, auch wenn sie keine Methode __iter__() definiert (sie verwendet das alte Iterable-Protokoll, das auf Basis von __len__() und __getitem__() definiert ist). Beachten Sie, dass get_iterator dadurch nicht als Methode von Foo verfügbar wird, daher wird sie separat bereitgestellt.

Das Modul abc stellt auch den folgenden Decorator bereit

@abc.abstractmethod

Ein Decorator, der abstrakte Methoden kennzeichnet.

Die Verwendung dieses Decorators erfordert, dass die Metaklasse der Klasse ABCMeta ist oder von ihr abgeleitet ist. Eine Klasse, die eine von ABCMeta abgeleitete Metaklasse hat, kann nicht instanziiert werden, es sei denn, alle ihre abstrakten Methoden und Eigenschaften werden überschrieben. Die abstrakten Methoden können über beliebige normale 'super'-Aufrufmechanismen aufgerufen werden. abstractmethod() kann verwendet werden, um abstrakte Methoden für Eigenschaften und Deskriptoren zu deklarieren.

Das dynamische Hinzufügen abstrakter Methoden zu einer Klasse oder der Versuch, den Abstraktionsstatus einer Methode oder Klasse nach ihrer Erstellung zu ändern, wird nur mit der Funktion update_abstractmethods() unterstützt. abstractmethod() beeinflusst nur Unterklassen, die über reguläre Vererbung abgeleitet wurden; "virtuelle Unterklassen", die mit der register()-Methode der ABC registriert wurden, sind nicht betroffen.

Wenn abstractmethod() in Kombination mit anderen Methoden-Deskriptoren verwendet wird, sollte es als innerster Decorator angewendet werden, wie in den folgenden Anwendungsbeispielen gezeigt

class C(ABC):
    @abstractmethod
    def my_abstract_method(self, arg1):
        ...
    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls, arg2):
        ...
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(arg3):
        ...

    @property
    @abstractmethod
    def my_abstract_property(self):
        ...
    @my_abstract_property.setter
    @abstractmethod
    def my_abstract_property(self, val):
        ...

    @abstractmethod
    def _get_x(self):
        ...
    @abstractmethod
    def _set_x(self, val):
        ...
    x = property(_get_x, _set_x)

Um korrekt mit der abstrakten Basisklassen-Mechanik zu interagieren, muss der Deskriptor sich selbst als abstrakt identifizieren, indem er __isabstractmethod__ verwendet. Im Allgemeinen sollte dieses Attribut True sein, wenn eine der Methoden, die zur Zusammensetzung des Deskriptors verwendet werden, abstrakt ist. Zum Beispiel führt die eingebaute property von Python das Äquivalent von

class Descriptor:
    ...
    @property
    def __isabstractmethod__(self):
        return any(getattr(f, '__isabstractmethod__', False) for
                   f in (self._fget, self._fset, self._fdel))

Hinweis

Im Gegensatz zu Java-abstrakten Methoden können diese abstrakten Methoden eine Implementierung haben. Diese Implementierung kann über den super()-Mechanismus von der Klasse aufgerufen werden, die sie überschreibt. Dies könnte als Endpunkt für einen Super-Aufruf in einem Framework nützlich sein, das kooperative Mehrfachvererbung verwendet.

Das Modul abc unterstützt auch die folgenden Legacy-Decoratoren

@abc.abstractclassmethod

Hinzugefügt in Version 3.2.

Veraltet seit Version 3.3: Es ist jetzt möglich, classmethod mit abstractmethod() zu verwenden, was diesen Decorator überflüssig macht.

Eine Unterklasse der eingebauten classmethod(), die eine abstrakte Klassenmethode kennzeichnet. Ansonsten ist sie ähnlich wie abstractmethod().

Dieser Sonderfall ist veraltet, da der Decorator classmethod() nun korrekt als abstrakt identifiziert wird, wenn er auf eine abstrakte Methode angewendet wird.

class C(ABC):
    @classmethod
    @abstractmethod
    def my_abstract_classmethod(cls, arg):
        ...
@abc.abstractstaticmethod

Hinzugefügt in Version 3.2.

Veraltet seit Version 3.3: Es ist jetzt möglich, staticmethod mit abstractmethod() zu verwenden, was diesen Decorator überflüssig macht.

Eine Unterklasse der eingebauten staticmethod(), die eine abstrakte statische Methode kennzeichnet. Ansonsten ist sie ähnlich wie abstractmethod().

Dieser Sonderfall ist veraltet, da der Decorator staticmethod() nun korrekt als abstrakt identifiziert wird, wenn er auf eine abstrakte Methode angewendet wird.

class C(ABC):
    @staticmethod
    @abstractmethod
    def my_abstract_staticmethod(arg):
        ...
@abc.abstractproperty

Veraltet seit Version 3.3: Es ist jetzt möglich, property, property.getter(), property.setter() und property.deleter() mit abstractmethod() zu verwenden, was diesen Decorator überflüssig macht.

Eine Unterklasse der eingebauten property(), die eine abstrakte Eigenschaft kennzeichnet.

Dieser Sonderfall ist veraltet, da der Decorator property() nun korrekt als abstrakt identifiziert wird, wenn er auf eine abstrakte Methode angewendet wird.

class C(ABC):
    @property
    @abstractmethod
    def my_abstract_property(self):
        ...

Das obige Beispiel definiert eine schreibgeschützte Eigenschaft; Sie können auch eine les- und schreibbare abstrakte Eigenschaft definieren, indem Sie eine oder mehrere der zugrunde liegenden Methoden entsprechend als abstrakt kennzeichnen.

class C(ABC):
    @property
    def x(self):
        ...

    @x.setter
    @abstractmethod
    def x(self, val):
        ...

Wenn nur einige Komponenten abstrakt sind, müssen nur diese Komponenten aktualisiert werden, um in einer Unterklasse eine konkrete Eigenschaft zu erstellen.

class D(C):
    @C.x.setter
    def x(self, val):
        ...

Das Modul abc unterstützt auch die folgenden Funktionen

abc.get_cache_token()

Gibt das aktuelle Token des abstrakten Basisklassen-Caches zurück.

Das Token ist ein opakes Objekt (das Gleichheitstests unterstützt) und identifiziert die aktuelle Version des abstrakten Basisklassen-Caches für virtuelle Unterklassen. Das Token ändert sich bei jedem Aufruf von ABCMeta.register() auf einer beliebigen ABC.

Hinzugefügt in Version 3.4.

abc.update_abstractmethods(cls)

Eine Funktion zur Neuberechnung des Abstraktionsstatus einer abstrakten Klasse. Diese Funktion sollte aufgerufen werden, wenn die abstrakten Methoden einer Klasse nach ihrer Erstellung implementiert oder geändert wurden. Normalerweise sollte diese Funktion innerhalb eines Klassen-Decorators aufgerufen werden.

Gibt cls zurück, um sie als Klassen-Decorator verwenden zu können.

Wenn cls keine Instanz von ABCMeta ist, tut die Funktion nichts.

Hinweis

Diese Funktion geht davon aus, dass die Oberklassen von cls bereits aktualisiert sind. Sie aktualisiert keine Unterklassen.

Hinzugefügt in Version 3.10.

Fußnoten