Можно также сделать 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
ADC: разрешить использование входного канала наchannel
set(channel)
реле: включить channel
/ выключить реле
ЦАП: установите выходное напряжение наchannel
ADC: хм ... ничего логичного в голову не приходит.
Общие глаголы становятся принудительными глаголами
Я бы сказал, что есть веские основания для использования вышеуказанных глаголов в модулях, поскольку мы видели, что их значение очевидно для каждого из них. Я бы продолжил писать свой базовый класс 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 спешит на помощь
Заполнив класс ADC следующим образом:
class ADC(Generic):
def set(self, channel):
raise NotImplementedError("Can't use 'set' on an ADC!")
Вы делаете сразу три очень хороших дела:
- Вы защищаете пользователя от ошибочного выполнения команды ('set'), которая не (и не должна!) Быть реализована для этого модуля.
- Вы им прямо говорите чем проблема (см. Ссылку TemporalWolf о «Голых исключениях», чтобы узнать, почему это важно)
- Вы защищаете реализацию всех других модулей, для которых обязательные глаголы
имеют смысл. Т.е. вы следите за тем, чтобы те модули, для которых эти глаголы делать имеет смысл будет реализовывать эти методы , и что они будут делать это , используя именно эти глаголы , а не некоторые другие Времнной имена.