Это на самом деле не имеет ничего общего с ABC, но с тем фактом, что вы восстанавливаете свойства в своем дочернем классе, но без установщиков.Это:
class ValueHistorical(Indicator):
@property
def db_ids(self):
return self._db_ids
@property
def name(self):
return self._name
Просто заменяет свойства родителя новыми, но определяет эти свойства только для чтения, поскольку вы не предоставили установщик.
Помните, что синтаксис декораторатолько синтаксический сахар, так что:
@property
def getter(...): pass
- это более изящный способ записи
def getter(...): pass
getter = property(getter)
Поскольку метод получения и метод установки являются атрибутами экземпляра property
, когда вы переопределяетесвойство в дочернем классе, вы не можете просто переопределить метод получения, вы также должны переопределить метод установки.
Распространенным примером здесь является делегирование метода получения и установки (если он есть) другому методу, поэтому вам не нужно переопределять все это, то есть:
class Base(object):
@property
def foo(self):
return self._get_foo()
@foo.setter
def foo(self, value):
self._set_foo(value)
def _get_foo(self):
# ...
def _set_foo(self, value):
# ...
Таким образом, дочерний класс может переопределять _get_foo
и / или _set_foo
без необходимости переопределять свойство.
Кроме того, применение property
и abstractmethod
к функции совершенно бесполезно.Это:
@property
@abstractmethod
def db_ids(self):
return self._db_ids
эквивалентно
def db_ids(self):
return self._db_ids
db_ids = property(abstractmethod(db_ids))
Так что ABC увидит здесь свойство - тот факт, что его геттер (и / или сеттер) были украшены abstractmethod
игнорируется, ABC не будет проверять метод получения и установки свойства.И если вы поставите их наоборот, то есть
db_ids = abstractmethod(property(db_ids))
, тогда вы не определите свойство вообще (на самом деле, оно не будет работать вообще - вы получите исключение с самого начала с«У объекта« property »нет атрибута» isabstractmethod '»)
FWIW, декоратор abstractmethod
предназначен только для использования с методами, которые НЕ определены (пустое тело), поэтомудочерние классы должны их реализовать.Если у вас есть реализация по умолчанию, не помечайте ее как абстрактную, иначе зачем вообще предоставлять реализацию по умолчанию?
РЕДАКТИРОВАТЬ:
Вы упомянули в комментарии (об удаленном ответе), что:
Я просто хочу, чтобы ValueHistorical перешел к методам установки класса Abstract для db_ids и name, когда они назначаются в конструкторе ValueHistorical
Тогда самое простое решение - этоодин, который я объяснил выше: определите методы реализации для метода получения и / или установки (вы можете сделать любой из них или оба абстрагировать, как считаете нужным) и использовать конкретное свойство для вызова этих методов реализации.
Oh ans yes: assert
- инструмент разработчика, не используйте его для проверки типов в рабочем коде.Если вы действительно хотите провести проверку типов (что иногда имеет смысл, но чаще всего не является пустой тратой времени), используйте isinstance
и поднимите TypeError
.Например, ваш db_ids
сеттер должен выглядеть следующим образом:
if not isinstance(ids, list):
raise TypeError("ids should be a list")
if not all(isinstance(id_, int) for id_ in ids)
raise TypeError("ids items should be ints")
или даже лучше:
# you don't care if it really was a list actually,
# as long as you can build a list out of it, and
# you don't care if it really contains ints as long
# as you can build ints out of them.
#
# No need for typecheck here, if `ids` is not iterable
# or what it yields cannot be used to build an int,
# this will raise, with way enough informations to
# debug the caller.
ids = [int(id) for id in ids)]