我已经为此奋斗了半个小时,所以我自己通过了尝试了半个小时的规则,并寻求您的帮助。我正在尝试让Child使用抽象类的setter抽象方法,但那样行不通...
#!/usr/bin/env python3
from abc import ABC, abstractmethod
from typing import List
class Indicator(ABC):
def __init__(self, **kwargs):
super().__init__()
pass
@abstractmethod
def calculate(self):
"""
kwargs in children will most likely be date_from, date_to, index
"""
raise NotImplementedError("The calculate method is not implemented!")
@property
@abstractmethod
def db_ids(self):
return self._db_ids
@db_ids.setter
@abstractmethod
def db_ids(self, ids: List[int]):
assert isinstance(ids, list)
assert all(isinstance(id_, int) for id_ in ids)
self._db_ids = ids
@property
@abstractmethod
def name(self):
return self._name
@name.setter
@abstractmethod
def name(self, set_name: str):
assert isinstance(set_name, str)
self._name = set_name
# …………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………
class ValueHistorical(Indicator):
def __init__(self, **kwargs):
if kwargs:
self.kwargs = kwargs
super(ValueHistorical, self).__init__(**kwargs)
self.db_ids = [119, 120, 121, 122]
self.name = 'Value Based on Historical'
@property
def db_ids(self):
return self._db_ids
@property
def name(self):
return self._name
def calculate(self):
pass
ValueHistorical(**{'date_from': '2010-01-01', 'date_to': '2012-01-01'})
这里的参数无关紧要。我得到的错误是AttributeError: can't set the attribute'
。
我想要实现的是在ValueHistorical构造函数中,它是分配给db_id和名称的父级抽象类的设置器。
答案 0 :(得分:3)
这实际上与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)
由于getter和setter是property
实例的属性,因此,当您在子类中重新定义属性时,您不仅可以重新定义getter,还必须重新定义setter。
这里常见的模式是让getter和setter(如果有的话)委派给另一种方法,因此您不必重新实现整个过程,即:
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构造函数中为其分配db_ids和name时,我基本上希望ValueHistorical转到Abstract类的setter方法
然后最简单的解决方案就是上面我解释过的解决方案:定义getter和/或setter的实现方法(您可以根据需要将它们中的任何一个或两者抽象化),并使用具体的属性来调用这些实现方法。
哦,是的: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)]
答案 1 :(得分:1)
要使用装饰器语法处理读/写抽象属性,获取和设置值的方法应命名为相同。
不要认为没有任何方法可以不需要设置者。但是IMO比将超类设置逻辑与fset
from abc import ABC, abstractmethod, abstractproperty
from typing import List
class Indicator(ABC):
def __init__(self, **kwargs):
super().__init__()
@abstractproperty
def db_ids(self):
return self._db_ids
@db_ids.setter
@abstractmethod
def db_ids(self, ids: List[int]):
self._db_ids = ids
class ValueHistorical(Indicator):
def __init__(self, **kwargs):
if kwargs:
self.kwargs = kwargs
super(ValueHistorical, self).__init__(**kwargs)
self.db_ids = [119, 120, 121, 122]
@property
def db_ids(self):
return self._db_ids
@db_ids.setter
def db_ids(self, ids: List[int]):
self._db_ids = ids
i = ValueHistorical(**{'date_from': '2010-01-01', 'date_to': '2012-01-01'})
print(i.db_ids)