Można również wykonać raise NotImplementedError()
wewnętrzną metodę @abstractmethod
-dekorowaną metodę klasy bazowej.
Wyobraź sobie, że piszesz skrypt sterujący dla rodziny modułów pomiarowych (urządzeń fizycznych). Funkcjonalność każdego modułu jest wąsko zdefiniowana, realizując tylko jedną dedykowaną funkcję: jednym może być szereg przekaźników, innym wielokanałowym przetwornikiem cyfrowo-analogowym lub przetwornikiem cyfrowo-analogowym, innym amperomierzowi itp.
Wiele z używanych poleceń niskiego poziomu byłoby współdzielonych między modułami, na przykład w celu odczytania ich numerów identyfikacyjnych lub wysłania do nich polecenia. Zobaczmy, co mamy w tym momencie:
Klasa podstawowa
from abc import ABC, abstractmethod
class Generic(ABC):
''' Base class for all measurement modules. '''
def __init__(self):
def _read_ID(self):
def _send_command(self, value):
Czasowniki wspólne
Następnie zdajemy sobie sprawę, że wiele czasowników poleceń specyficznych dla modułu, a zatem logika ich interfejsów jest również współdzielona. Oto 3 różne czasowniki, których znaczenie byłoby oczywiste, biorąc pod uwagę liczbę modułów docelowych.
get(channel)
przekaźnik: uzyskaj stan włączenia / wyłączenia przekaźnikachannel
DAC: uzyskać wyjściowego napięcia nachannel
ADC: uzyskać wejściowego napięcia nachannel
enable(channel)
przekaźnik: włącz użycie przekaźnikachannel
DAC: włącz użycie kanału wyjściowegochannel
ADC: włącz używanie kanału wejściowegochannel
set(channel)
przekaźnik:channel
włączanie / wyłączanie przekaźnika
DAC: ustawienie wyjściowe napięcie nachannel
ADC: hmm ... nic logicznego nie przychodzi na myśl.
Czasowniki współdzielone stają się czasownikami wymuszonymi
Twierdzę, że powyższe czasowniki powinny być współużytkowane przez moduły, ponieważ widzieliśmy, że ich znaczenie jest oczywiste dla każdego z nich. Kontynuowałbym pisanie mojej klasy bazowej w ten Generic
sposób:
class Generic(ABC):
@abstractmethod
def get(self, channel):
pass
@abstractmethod
def enable(self, channel):
pass
@abstractmethod
def set(self, channel):
pass
Podklasy
Teraz wiemy, że wszystkie nasze podklasy będą musiały zdefiniować te metody. Zobaczmy, jak mógłby wyglądać moduł ADC:
class ADC(Generic):
def __init__(self):
super().__init__()
def get(self, channel):
def enable(self, channel):
Możesz się teraz zastanawiać:
Ale to nie zadziała dla modułu ADC , ponieważ set
nie ma to sensu, ponieważ widzieliśmy to powyżej!
Masz rację: brak implementacji set
nie jest opcją, ponieważ Python wywołałby poniższy błąd podczas próby utworzenia instancji obiektu ADC.
TypeError: Can't instantiate abstract class 'ADC' with abstract methods 'set'
Więc trzeba zaimplementować coś, bo zrobiliśmy egzekwowane czasownik (aka „@abstractmethod”), który jest wspólny dla dwóch innych modułów, ale w tym samym czasie, należy również wdrożyć jako nic nie
nie ma sensu dla tego konkretnego modułu.set
set
NotImplementedError to the Rescue
Ukończenie klasy ADC w ten sposób:
class ADC(Generic):
def set(self, channel):
raise NotImplementedError("Can't use 'set' on an ADC!")
Robisz jednocześnie trzy bardzo dobre rzeczy:
- Chronisz użytkownika przed błędnym wydaniem polecenia („set”), które nie jest (i nie powinno!) Być zaimplementowane dla tego modułu.
- Mówisz im wyraźnie, na czym polega problem (patrz link TemporalWolf o `` Nagich wyjątkach '', aby dowiedzieć się, dlaczego jest to ważne)
- Chronisz implementację wszystkich innych modułów, dla których egzekwowane czasowniki
mają sens. Oznacza to, że upewniasz się, że te moduły, dla których te czasowniki mają sens, zaimplementują te metody i że będą to robić przy użyciu dokładnie tych czasowników, a nie innych nazw ad-hoc.