Szukaj…


Ustawianie metaklasy ABCMeta

Klasy abstrakcyjne to klasy, które mają być dziedziczone, ale unikają implementacji określonych metod, pozostawiając jedynie sygnatury metod, które muszą zostać zaimplementowane w podklasach.

Klasy abstrakcyjne są przydatne do definiowania i egzekwowania abstrakcji klas na wysokim poziomie, podobnie jak koncepcja interfejsów w językach pisanych na maszynie, bez potrzeby implementacji metod.

Jednym koncepcyjnym podejściem do definiowania klasy abstrakcyjnej jest wyeliminowanie metod klas, a następnie zgłoszenie błędu NotImplementedError, jeśli jest dostępny. Zapobiega to dostępowi klas potomnych do metod nadrzędnych bez uprzedniego ich przesłonięcia. Tak jak:

class Fruit:
    
    def check_ripeness(self):
        raise NotImplementedError("check_ripeness method not implemented!")


class Apple(Fruit):
    pass


a = Apple()
a.check_ripeness() # raises NotImplementedError

Utworzenie w ten sposób klasy abstrakcyjnej zapobiega niewłaściwemu użyciu metod, które nie są nadpisywane, i z pewnością zachęca do definiowania metod w klasach potomnych, ale nie wymusza ich definicji. Dzięki modułowi abc możemy zapobiegać tworzeniu instancji klas potomnych, gdy nie zastąpią one metod klas abstrakcyjnych swoich rodziców i przodków:

from abc import ABCMeta

class AbstractClass(object):
    # the metaclass attribute must always be set as a class variable 
    __metaclass__ = ABCMeta

   # the abstractmethod decorator registers this method as undefined
   @abstractmethod 
   def virtual_method_subclasses_must_define(self):
       # Can be left completely blank, or a base implementation can be provided
       # Note that ordinarily a blank interpretation implicitly returns `None`, 
       # but by registering, this behaviour is no longer enforced.

Teraz można po prostu podklasować i zastępować:

class Subclass(AbstractClass):
    def virtual_method_subclasses_must_define(self):
        return

Dlaczego / Jak korzystać z ABCMeta i @abstractmethod

Abstrakcyjne klasy podstawowe (ABC) egzekwują, które klasy pochodne implementują określone metody z klasy podstawowej.

Aby zrozumieć, jak to działa i dlaczego powinniśmy z niego korzystać, spójrzmy na przykład, który spodoba się Van Rossum. Załóżmy, że mamy klasę podstawową „MontyPython” z dwiema metodami (żart i poncz), które muszą zostać zaimplementowane przez wszystkie klasy pochodne.

class MontyPython:
    def joke(self):
        raise NotImplementedError()

    def punchline(self):
        raise NotImplementedError()

class ArgumentClinic(MontyPython):
    def joke(self):
        return "Hahahahahah"

Kiedy tworzymy instancję obiektu i wywołujemy dwie metody, otrzymujemy błąd (zgodnie z oczekiwaniami) z metodą punchline() .

 >>> sketch = ArgumentClinic() 
 >>> sketch.punchline() 
NotImplementedError 

Jednak nadal pozwala nam to tworzyć instancję obiektu klasy ArgumentClinic bez otrzymywania błędu. W rzeczywistości nie pojawia się błąd, dopóki nie szukamy punchline ().

Można tego uniknąć, stosując moduł abstrakcyjnej klasy podstawowej (ABC). Zobaczmy, jak to działa na tym samym przykładzie:

from abc import ABCMeta, abstractmethod

class MontyPython(metaclass=ABCMeta):
    @abstractmethod
    def joke(self):
        pass

@abstractmethod
def punchline(self):
    pass

class ArgumentClinic(MontyPython):
    def joke(self):
        return "Hahahahahah"

Tym razem, gdy próbujemy utworzyć instancję obiektu z niekompletnej klasy, natychmiast otrzymujemy błąd TypeError!

>>> c = ArgumentClinic()
TypeError:
"Can't instantiate abstract class ArgumentClinic with abstract methods punchline"

W takim przypadku łatwo jest ukończyć klasę, aby uniknąć błędów typu:

class ArgumentClinic(MontyPython):
    def joke(self):
        return "Hahahahahah"

    def punchline(self):
        return "Send in the constable!"

Tym razem po utworzeniu wystąpienia obiektu działa!



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow