Python Language
Abstrakcyjne klasy podstawowe (abc)
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!