Моя ситуация примерно такая:
class AbstractClass:
def __init__(self, property_a):
self.property_a = property_a
@property
def some_value(self):
"""Code here uses property_a but not property_b to determine some_value"""
@property
def property_a(self):
return self.property_a
@property
def property_b(self):
"""Has to be implemented in subclass."""
raise NotImplementedError
class Concrete1(AbstractClass):
"""Code here including an implementation of property_b"""
class Concrete2(AbstractClass):
"""Code here including an implementation of property_b"""
Также существует условие, что если property_b
меньше, чем property_a
, то property_a
недействителен и, следовательно, результат some_value
также недействителен.
Я имею в виду, что ... если в любой момент времени существования объекта вызов property_b
даст число меньше, чем вызов property_a
, возникнет проблема. Однако свойство property_b не является полем . Он определяется динамически на основе полей n , где n > = 1. Невозможно проверить это условие при установке property_b
, потому что само property_b
никогда не устанавливается. На самом деле, сеттеры не предполагается использовать где-нибудь здесь. Все поля, скорее всего, будут установлены в конструкторах, а затем оставлены в покое. Это означает, что property_a
будет известен в конструкторе для AbstractClass
и property_b
только после оценки конструктора для конкретных классов.
<обновление>
Основная проблема заключается в следующем: мне нужно проверить property_a
на валидность, но когда установлено property_a
(наиболее интуитивно понятное место для проверки), property_b
не определено.
Я хочу убедиться, что property_b
никогда не будет меньше, чем property_a
. Как мне с этим справиться?
Проверить property_a
на property_b
в ...
AbstractClass.__init__
. На самом деле это невозможно, потому чтоproperty_b
еще не определен.AbstractClass.property_a
. Это кажется проблематичным, потому что я выбрасываю исключение в геттере.- Каждая конкретная реализация
property_b
. Я бы не только генерировал исключение в геттере, но и дублировал код. Такжеproperty_b
логически не зависит отproperty_a
. AbstractClass.some_value
. Это по-прежнему вызывает исключение в получателе. Кроме того, логически невозможно, чтобыproperty_b
было меньше, чемproperty_a
все время , не только при попытке определитьsome_value
. Кроме того, если подклассы решат добавить другие свойства, зависящие отproperty_a
, они могут забыть сверить его сproperty_b
.- Бетоноукладчики для
property_b
. Такого не существует.property_b
иногда определяется из значения, установленного в конструкторе, иногда вычисляется из нескольких значений. Также дублирование кода. - Конкретные методы класса
__init__
. Дублирование кода. Кто-то может забыть. - ???
ОБНОВЛЕНИЕ
Я думаю, что путаницу вызывает то, что property_b
- это не просто поле. property_b
полагается на расчеты. Это действительно больше функция, чем свойство, если это помогает думать об этом таким образом.
3 ответа
Добавьте метод _validate_b(self, b)
(одинарное подчеркивание в начале для обозначения «защищенного», т. Е. Вызываемого из производных классов, но не из общего клиентского кода), который проверяет значение b (которое известно только подклассам) по сравнению со значением a (которое абстрактный суперкласс знает).
Сделайте подклассы ответственными за вызов метода проверки всякий раз, когда они делают что-то, что может изменить их внутреннее значение для b. В вашем очень общем случае суперкласс не может определить, когда значение b изменяется; поскольку ответственность за это изменение полностью лежит на подклассах, то ответственность за запуск проверки также должна быть на них (суперкласс может выполнить проверку, учитывая предложенное новое значение b, но он не может знать < em> когда необходимо проверить правильность). Итак, четко задокументируйте это.
Если большинство подклассов попадают в широкие категории с точки зрения стратегий, которые они используют для воздействия на их значения b, вы можете разделить это на любой из промежуточных абстрактных классов (унаследованных от общего и специализирующихся на общем подходе к «определению b», включая проверку call) или (лучше, если это возможно) какой-либо формы шаблона проектирования стратегии (обычно реализуемой либо через композиционное, либо смешанное наследование). Но это больше связано с удобством (для авторов конкретных подклассов), чем с «гарантией правильности», поскольку данный конкретный подкласс может обходить такие механизмы.
Если вам нужно, вы можете предложить «режим отладки / тестирования», в котором свойства проверяются с избыточностью при доступе (вероятно, не рекомендуется в производственном использовании, но для отладки и тестирования это поможет отловить ошибочные подклассы, которые неправильно вызывают методы проверки).
Золотое правило - «инкапсулировать» property_b
так, чтобы подкласс обеспечивал часть реализации, но не всю ее.
class AbstractClass:
def __init__(self, property_a):
self._value_of_a = property_a
@property
def get_b( self ):
self.validate_a()
self._value_of_b = self.compute_b()
self.validate_other_things()
return self._value_of_b
def compute_b( self ):
raise NotImplementedError
Трудно сказать точно, что должно произойти, когда у вас два класса и вы спрашиваете о распределении ответственности.
Похоже, вы хотите, чтобы суперкласс отвечал за некоторые аспекты отношений между a и b.
Похоже, что вы хотите, чтобы подкласс отвечал за какой-то другой аспект вычисления b, а не за отношения.
Если это то, что вы хотите, тогда в вашем проекте должна быть возложена ответственность путем декомпозиции вещей на часть, за которую отвечает суперкласс, и на часть, за которую отвечает подкласс.
Я предлагаю вам не вызывать raise NotImplementedError
, а вместо этого вызвать метод, который вызывает эту ошибку. Затем подклассы должны переопределить этот метод (вместо property_b
). В property_b
вы вызываете метод, а затем проверяете результат.
Обоснование: вам следует проверить значение как можно скорее (когда кто-то его изменит). В противном случае может быть установлено недопустимое значение, что вызовет проблему намного позже в коде, когда никто не может сказать, как оно туда попало.
В качестве альтернативы вы можете сохранить значение и трассировку стека. Когда значение используется, вы можете затем проверить значение и распечатать исходную трассировку стека, поскольку «здесь было изменено значение».
Похожие вопросы
Новые вопросы
python
Python - это многопарадигмальный, динамически типизированный, многоцелевой язык программирования. Он разработан для быстрого изучения, понимания и использования, а также для обеспечения чистого и единообразного синтаксиса. Обратите внимание, что Python 2 официально не поддерживается с 01.01.2020. Тем не менее, для вопросов о Python, связанных с версией, добавьте тег [python-2.7] или [python-3.x]. При использовании варианта Python (например, Jython, PyPy) или библиотеки (например, Pandas и NumPy) включите его в теги.