我的情况是这样的:
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
本身永远不会被设定。实际上,预计不会在这里任何地方使用setter。所有字段都可能在构造函数中设置,然后单独保留。这意味着在评估具体类的构造函数之后,property_a
的构造函数中将AbstractClass
和property_b
只知道。
<更新>
根本问题在于:我需要检查property_a
的有效性,但设置property_a
时(最直观的检查位置),property_b
未定义。
的< /更新>
我想确保property_b
永远不会低于property_a
。我该怎么处理呢?
在... {/ p>中对property_a
进行property_b
检查
AbstractClass.__init__
。这实际上是不可能的,因为property_b
尚未定义。AbstractClass.property_a
。这似乎有问题,因为我会在getter中抛出异常。property_b
的每个具体实现。我不仅会在getter中抛出异常,我还会复制代码。此外,property_b
在逻辑上不依赖于property_a
。AbstractClass.some_value
。这仍然在getter中引发异常。此外,property_b
逻辑上不可能始终小于property_a
,而不仅仅是在尝试确定some_value
时。此外,如果子类决定添加依赖于property_a
的其他属性,他们可能会忘记对property_b
进行检查。property_b
的具体制定者。这些不存在。 property_b
有时是根据构造函数中的值集确定的,有时是根据多个值计算的。另外,代码重复。__init__
方法。代码重复。有人可能会忘记。更新
我认为导致混淆的是property_b
不仅仅是一个领域。 property_b
依赖于计算。它实际上是一个功能而不是一个属性,如果它有助于以这种方式思考它。
答案 0 :(得分:3)
添加方法_validate_b(self, b)
(单个前导下划线表示“受保护”,即可从派生类调用,但不能通过一般客户端代码调用),验证b的值(只有子类知道)与值a(抽象超类确实知道)。
当子类做出可能改变b内部值的事情时,让子类负责调用验证方法。在一般情况下,超类无法识别b的值何时发生变化;由于该变更的责任完全在于子类,因此触发验证的责任也必须与它们一起(超类可以执行验证,给定b的新值,但它不能知道< em>必须检查有效期时)。所以,清楚地记录下来。
如果大多数子类在用于影响其b值的策略方面属于广义类别,则可以将其分解为中间抽象类(继承自一般类别并专门化“确定b”的一般方法,包括验证调用),或(更好,如果可行的话)某种形式的策略设计模式(通常通过组合或混合继承实现)。但这与方便性(对于具体子类作者)有关,而与“保证正确性”有关,因为给定的具体子类可能绕过这种机制。
如果需要,可以提供“调试/测试模式”,其中属性在访问时被冗余验证(在生产使用中可能不可取,但是对于调试和测试,它将有助于捕获未正确调用验证的错误子类方法)。
答案 1 :(得分:1)
黄金法则是“封装”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的其他方面,而不负责这种关系。
如果这是您想要的,那么您的设计必须通过将事物分解为超类负责的部分以及子类负责的部分来分配责任。
答案 2 :(得分:0)
我建议您不要引发raise NotImplementedError
,而是调用一个方法来代替引发此错误。然后子类必须覆盖该方法(而不是property_b
)。在property_b
中,您调用该方法,然后验证结果。
理由:您应该尽快检查该值(即有人更改时)。否则,可能会设置一个非法值,并且在没有人能说出它是如何到达那里时会在代码中导致问题。
或者,您可以存储值和堆栈跟踪。使用该值时,您可以检查该值并将原始堆栈跟踪打印为“此处更改了值”。