Можна також зробити raise NotImplementedError()
внутрішній дочірній метод методу- @abstractmethod
декорованого базового класу.
Уявіть, що ви пишете сценарій управління для сімейства вимірювальних модулів (фізичних пристроїв). Функціональність кожного модуля є вузько визначеною, реалізуючи лише одну спеціальну функцію: одна може бути масивом реле, інша - багатоканальним ЦАП або АЦП, інша - амперметром тощо.
Значна частина команд низького рівня, що використовуються, буде спільно використовуватися між модулями, наприклад, для зчитування їх ідентифікаційних номерів або надсилання їм команди. Давайте подивимось, що ми маємо на даний момент:
Базовий клас
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):
Спільні дієслова
Потім ми усвідомлюємо, що велика частина модульних командних дієслів і, отже, логіка їх інтерфейсів також є спільною. Ось 3 різні дієслова, значення яких є зрозумілим, враховуючи низку цільових модулів.
get(channel)
реле: увімкнути / вимкнути статус релеchannel
ЦАП: увімкніть вихідну напругуchannel
АЦП: увімкніть вхідну напругуchannel
enable(channel)
реле: увімкнути використання релеchannel
ЦАП: увімкнути використання вихідного каналуchannel
АЦП: увімкнути використання вхідного каналуchannel
set(channel)
реле:channel
увімкнення / вимкнення реле
ЦАП: увімкнути вихідну напругуchannel
АЦП: хм ... нічого логічного не спадає на думку.
Спільні дієслова стають вимушеними дієсловами
Я міг би стверджувати, що існує сильний випадок, коли вищезазначені дієслова можна поділити між модулями, оскільки ми побачили, що їх значення очевидне для кожного з них. Я продовжував би писати свій базовий клас Generic
так:
class Generic(ABC):
@abstractmethod
def get(self, channel):
pass
@abstractmethod
def enable(self, channel):
pass
@abstractmethod
def set(self, channel):
pass
Підкласи
Зараз ми знаємо, що всі наші підкласи повинні будуть визначати ці методи. Давайте подивимося, як це може виглядати для модуля ADC:
class ADC(Generic):
def __init__(self):
super().__init__()
def get(self, channel):
def enable(self, channel):
Вам зараз може бути цікаво:
Але це не буде працювати для модуля ADC, оскільки set
там немає сенсу, як ми щойно бачили це вище!
Ви маєте рацію: не реалізація set
- це не варіант, оскільки Python тоді запускає помилку нижче, коли ви намагалися створити екземпляр об’єкта ADC.
TypeError: Can't instantiate abstract class 'ADC' with abstract methods 'set'
Таким чином , ви повинні реалізувати що - то, тому що ми зробили виконання дієслова ( так званим «@abstractmethod»), який є загальним для двох інших модулів , але в той же час, ви також не повинні нічого реалізувати , як
не має сенсу для даного конкретного модуля.set
set
NotImplementedError to the Rescue
Заповнивши клас ADC таким чином:
class ADC(Generic):
def set(self, channel):
raise NotImplementedError("Can't use 'set' on an ADC!")
Ви робите три дуже гарні речі одночасно:
- Ви захищаєте користувача від помилкової команди ('set'), яка не (і не повинна!) Бути реалізованою для цього модуля.
- Ви прямо говорите їм, у чому проблема (див. Посилання TemporalWolf про "Голі винятки", чому це важливо)
- Ви захищаєте реалізацію всіх інших модулів, для яких вимушені дієслова
мають сенс. Тобто ви гарантуєте, що ті модулі, для яких ці дієслова мають сенс, будуть реалізовувати ці методи, і що вони будуть робити це, використовуючи саме ці дієслова, а не деякі спеціальні імена.