abc — Abstrakta basklasser

Källkod: Lib/abc.py


Denna modul tillhandahåller infrastrukturen för att definiera abstract base classes (ABC) i Python, enligt PEP 3119; se PEP:en för varför detta lades till i Python. (Se även PEP 3141 och modulen numbers angående en typhierarki för tal baserad på ABC)

Modulen collections har några konkreta klasser som härstammar från ABC; dessa kan naturligtvis härledas ytterligare. Dessutom har undermodulen collections.abc några ABC som kan användas för att testa om en klass eller instans tillhandahåller ett visst gränssnitt, t.ex. om den är hashable eller om den är en mapping.

Denna modul tillhandahåller metaklassen ABCMeta för att definiera ABC och en hjälpklass ABC för att alternativt definiera ABC genom arv:

class abc.ABC

En hjälpklass som har ABCMeta som sin metaklass. Med denna klass kan en abstrakt basklass skapas genom att helt enkelt härleda från ABC, vilket undviker ibland förvirrande användning av metaklasser, till exempel:

från abc import ABC

klass MyABC(ABC):
    pass

Observera att typen för ABC fortfarande är ABCMeta, och att ärva från ABC kräver därför de vanliga försiktighetsåtgärderna beträffande användning av metaklasser, eftersom flerfaldigt arv kan leda till metaklasskonflikter. Man kan också definiera en abstrakt basklass genom att skicka nyckelordet metaclass och använda ABCMeta direkt, till exempel:

from abc import ABCMeta

class MyABC(metaclass=ABCMeta):
    pass

Tillagd i version 3.4.

class abc.ABCMeta

Metaklass för att definiera abstrakta basklasser (ABC).

Använd denna metaklass för att skapa en ABC. En ABC kan subklassas direkt och fungerar då som en mix-in klass. Du kan också registrera orelaterade konkreta klasser (även inbyggda klasser) och orelaterade ABC som ”virtuella underklasser” – dessa och deras ättlingar kommer att betraktas som underklasser till den registrerande ABC av den inbyggda issubclass()-funktionen, men den registrerande ABC kommer inte att dyka upp i deras MRO (Method Resolution Order) och inte heller kommer metodimplementationer som definieras av den registrerande ABC att kunna anropas (inte ens via super()). [1]

Klasser som skapats med en metaklass av ABCMeta har följande metod:

register(subclass)

Registrera subclass som en ”virtuell subclass” av denna ABC. Till exempel:

från abc import ABC

klass MyABC(ABC):
    pass

MyABC.register(tuple)

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

Ändrad i version 3.3: Returnerar den registrerade subklassen, så att den kan användas som en klassdekorator.

Ändrad i version 3.4: För att upptäcka anrop till register() kan du använda funktionen get_cache_token().

Du kan också åsidosätta den här metoden i en abstrakt basklass:

__subclasshook__(subclass)

(Måste definieras som en klassmetod.)

Kontrollera om subclass anses vara en subklass av denna ABC. Detta innebär att du kan anpassa beteendet hos issubclass() ytterligare utan att behöva anropa register() på varje klass du vill betrakta som en underklass till ABC. (Denna klassmetod anropas från ABC:s metod __subclasscheck__())

Denna metod bör returnera True, False eller NotImplemented. Om den returnerar True, anses subclass vara en subklass av denna ABC. Om den returnerar False anses subclass inte vara en subklass av denna ABC, även om den normalt skulle vara det. Om den returnerar NotImplemented, fortsätter underklasskontrollen med den vanliga mekanismen.

För en demonstration av dessa begrepp, se detta exempel på ABC-definition:

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

klass MyIterable(ABC):

    @abstraktmetod
    def __iter__(self):
        medan False:
            yield None

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

    @klassmetod
    def __subclasshook__(cls, C):
        om cls är MyIterable:
            if any("__iter__" in B.__dict__ for B in C.__mro__):
                return True
        return Inte implementerad

MyIterable.register(Foo)

ABC MyIterable definierar standardmetoden för iterable, __iter__(), som en abstrakt metod. Den implementation som ges här kan fortfarande anropas från subklasser. Metoden get_iterator() är också en del av den abstrakta basklassen MyIterable, men den behöver inte åsidosättas i icke-abstrakta härledda klasser.

Klassmetoden __subclasshook__() som definieras här säger att alla klasser som har en __iter__()-metod i sin __dict__ (eller i en av dess basklasser, som nås via listan __mro__) också betraktas som en MyIterable.

Slutligen, den sista raden gör Foo till en virtuell subklass av MyIterable, även om den inte definierar en __iter__()-metod (den använder det gamla iterabelprotokollet, definierat i termer av __len__() och __getitem__()). Observera att detta inte kommer att göra get_iterator tillgänglig som en metod i Foo, så den tillhandahålls separat.

Modulen abc innehåller också följande dekorator:

@abc.abstractmethod

En dekorator som anger abstrakta metoder.

För att använda denna dekorator krävs att klassens metaklass är ABCMeta eller är härledd från den. En klass som har en metaklass som härrör från ABCMeta kan inte instansieras om inte alla dess abstrakta metoder och egenskaper åsidosätts. De abstrakta metoderna kan anropas med hjälp av någon av de normala ”super”-anropsmekanismerna. abstractmethod() kan användas för att deklarera abstrakta metoder för egenskaper och deskriptorer.

Att dynamiskt lägga till abstrakta metoder i en klass, eller att försöka ändra abstraktionsstatusen för en metod eller klass när den väl har skapats, stöds endast med funktionen update_abstractmethods(). Funktionen abstractmethod() påverkar endast underklasser som härletts med hjälp av vanligt arv; ”virtuella underklasser” som registrerats med ABC:s metod register() påverkas inte.

När abstractmethod() används i kombination med andra metodbeskrivare bör den användas som den innersta dekoratorn, vilket visas i följande användningsexempel:

klass C(ABC):
    @abstraktmetod
    def my_abstract_method(self, arg1):
        ...
    @klassmetod
    @abstraktmetod
    def my_abstract_classmethod(cls, arg2):
        ...
    @statiskmetod
    @abstraktmetod
    def my_abstract_staticmethod(arg3):
        ...

    @egenskap
    @abstraktmetod
    def my_abstract_property(self):
        ...
    @my_abstract_property.setter
    @abstraktmetod
    def my_abstract_property(self, val):
        ...

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

För att korrekt kunna samverka med den abstrakta basklassens maskineri måste deskriptorn identifiera sig själv som abstrakt med hjälp av __isabstractmethod__. I allmänhet bör detta attribut vara True om någon av de metoder som används för att komponera deskriptorn är abstrakt. Till exempel gör Pythons inbyggda property motsvarande:

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

Anteckning

Till skillnad från Javas abstrakta metoder kan dessa abstrakta metoder ha en implementation. Denna implementation kan anropas via super()-mekanismen från den klass som åsidosätter den. Detta kan vara användbart som en slutpunkt för ett superanrop i ett ramverk som använder kooperativ multipel nedärvning.

Modulen abc har även stöd för följande äldre dekoratorer:

@abc.abstractclassmethod

Tillagd i version 3.2.

Föråldrad sedan version 3.3: Det är nu möjligt att använda classmethod med abstractmethod(), vilket gör denna dekorator överflödig.

En subklass av den inbyggda classmethod(), vilket indikerar en abstrakt klassmetod. Annars liknar den abstractmethod().

Detta specialfall är inte längre aktuellt, eftersom classmethod()-dekoratorn nu identifieras korrekt som abstrakt när den tillämpas på en abstrakt metod:

klass C(ABC):
    @klassmetod
    @abstraktmetod
    def my_abstract_classmethod(cls, arg):
        ...
@abc.abstractstaticmethod

Tillagd i version 3.2.

Föråldrad sedan version 3.3: Det är nu möjligt att använda staticmethod med abstractmethod(), vilket gör denna dekorator överflödig.

En subklass av den inbyggda staticmethod(), vilket indikerar en abstrakt staticmethod. Annars liknar den abstractmethod().

Detta specialfall är inte längre aktuellt, eftersom staticmethod()-dekoratorn nu identifieras korrekt som abstrakt när den tillämpas på en abstrakt metod:

klass C(ABC):
    @statiskmetod
    @abstraktmetod
    def my_abstract_staticmethod(arg):
        ...
@abc.abstractproperty

Föråldrad sedan version 3.3: Det är nu möjligt att använda property, property.getter(), property.setter() och property.deleter() med abstractmethod(), vilket gör denna dekorator överflödig.

En subklass av den inbyggda property(), som anger en abstrakt egenskap.

Detta specialfall är inte längre aktuellt, eftersom property()-dekoratorn nu identifieras korrekt som abstrakt när den tillämpas på en abstrakt metod:

klass C(ABC):
    @egenskap
    @abstraktmetod
    def my_abstract_property(self):
        ...

I exemplet ovan definieras en skrivskyddad egenskap; du kan också definiera en abstrakt skrivskyddad egenskap genom att på lämpligt sätt markera en eller flera av de underliggande metoderna som abstrakt:

klass C(ABC):
    @egenskap
    def x(self):
        ...

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

Om endast vissa komponenter är abstrakta, behöver endast dessa komponenter uppdateras för att skapa en konkret egenskap i en underklass:

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

Modulen abc innehåller även följande funktioner:

abc.get_cache_token()

Returnerar den aktuella cachetoken för abstrakta basklasser.

Token är ett opakt objekt (som stöder likhetstestning) som identifierar den aktuella versionen av den abstrakta basklassens cache för virtuella underklasser. Token ändras med varje anrop till ABCMeta.register() på valfri ABC.

Tillagd i version 3.4.

abc.update_abstractmethods(cls)

En funktion för att räkna om en abstrakt klass abstraktionsstatus. Denna funktion bör anropas om en klass abstrakta metoder har implementerats eller ändrats efter att den skapades. Vanligtvis bör denna funktion anropas inifrån en klassdekorator.

Returnerar cls, så att den kan användas som en klassdekorator.

Om cls inte är en instans av ABCMeta, gör ingenting.

Anteckning

Denna funktion förutsätter att clss superklasser redan är uppdaterade. Den uppdaterar inte några underklasser.

Tillagd i version 3.10.

Fotnoter