[英]Where should I check state / throw exception?
My situation is something like this: 我的情况是这样的:
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"""
There is also a condition that if property_b
is less than property_a
, then property_a
is invalid and thus the result of some_value
is also invalid. 还有一个条件是如果
property_b
小于property_a
,则property_a
无效,因此some_value
的结果也无效。
What I mean is this... if at any time during the object's lifetime, calling property_b
would yield a number lower than calling property_a
, there's a problem. 我的意思是这个......如果在对象的生命周期中的任何时候,调用
property_b
会产生比调用property_a
更低的数字,那就有问题了。 However, property_b is not a field . 但是,property_b 不是字段 。 It is determined dynamically based on n fields, where n >= 1. It is impossible to check this condition while setting
property_b
because property_b
itself is never set. 它是基于n个字段动态确定的,其中n > = 1.在设置
property_b
无法检查此条件,因为property_b
本身从未设置。 Really, setters are not anticipated to be used anywhere here. 实际上,预计不会在这里任何地方使用制定者。 All fields are likely to be set in the constructors and then left alone.
所有字段都可能在构造函数中设置,然后单独保留。 This means that
property_a
will be known in the constructor for AbstractClass
and property_b
only after evaluating the constructor for the concrete classes. 这意味着,
property_a
将在构造函数中被称为AbstractClass
和property_b
仅评估构造函数的具体类之后 。
<update> <更新>
The fundamental problem is this: I need to check property_a
for validity, but when property_a
is set (the most intuitive place to check it), property_b
is undefined. 基本问题是:我需要检查
property_a
的有效性,但是当设置property_a
时(最直观的检查位置), property_b
是未定义的。
</update> </更新>
I want to ensure that property_b
is never less than property_a
. 我想确保
property_b
永远不会低于property_a
。 How should I handle it? 我应该怎么处理?
Check property_a
against property_b
in... 在...中检查
property_a
property_b
AbstractClass.__init__
. AbstractClass.__init__
。 This is actually impossible because property_b
hasn't been defined yet. property_b
。 AbstractClass.property_a
. AbstractClass.property_a
。 This seems problematic because I would be throwing an exception in a getter. property_b
. property_b
每个具体实现。 Not only would I be throwing an exception in a getter, I would be duplicating code. property_b
does not logically depend on property_a
. property_b
在逻辑上也不依赖于property_a
。 AbstractClass.some_value
. AbstractClass.some_value
。 This is still throwing an exception in a getter. property_b
to be less than property_a
all the time , not just when trying to determine some_value
. property_b
比少property_a
所有的时间 ,试图确定不只是当some_value
。 Further, if subclasses decide to add other properties that depend on property_a
, they may forget to check it against property_b
. property_a
其他属性,他们可能会忘记对property_b
进行检查。 property_b
. property_b
具体setter。 These don't exist. property_b
is sometimes determined from a value set in the constructor, sometimes calculated from multiple values. property_b
有时是根据构造函数中设置的值确定的,有时是根据多个值计算的。 Also, code duplication. __init__
methods. __init__
方法。 Code duplication. UPDATE UPDATE
I think what is causing confusion is that property_b
is not simply a field. 我认为导致混淆的是
property_b
不仅仅是一个领域。 property_b
relies on calculations. property_b
依赖于计算。 It is really more a function than a property, if it helps to think about it that way. 它实际上是一个功能而不是一个属性,如果它有助于以这种方式思考它。
Add a method _validate_b(self, b)
(single leading underscore to indicate "protected", ie, callable from derived classes but not by general client code) that validates the value of b (which only subclasses know) vs the value of a (which the abstract superclass does know). 添加方法
_validate_b(self, b)
(单个前导下划线表示“受保护”,即可从派生类调用,但不能通过一般客户端代码调用),验证b的值(只有子类知道)与a的值(抽象超类确实知道的)。
Make subclasses responsible for calling the validation method whenever they're doing something that could change their internal value for b. 让子类负责在他们做某些可能改变b的内部值的事情时调用验证方法。 In your very general case, the superclass cannot identify when b's value changes;
在一般情况下,超类无法识别b的值何时发生变化; since the responsibility of that change lies entirely with the subclasses, then the responsibility for triggering validation must also be with them (the superclass can perform validation, given the proposed new value of b, but it cannot know when validity must be checked).
由于该变更的责任完全在于子类,因此触发验证的责任也必须与它们一起(超类可以执行验证,给定b的新值,但它不知道何时必须检查有效性)。 So, document that clearly.
所以,清楚地记录下来。
If most subclasses fall into broad categories in terms of the strategies they use to affect their b's values, you can factor that out into either intermediate abstract classes (inheriting from the general one and specializing the general approach to "determining b", including the validation call), or (better, if feasible) some form of Strategy design pattern (typically implemented via either composition, or mix-in inheritance). 如果大多数子类在用于影响其b值的策略方面属于广泛的类别,则可以将其分解为中间抽象类(从一般类继承并专门化“确定b”的一般方法,包括验证调用),或(更好,如果可行的话)某种形式的策略设计模式(通常通过组合或混合继承实现)。 But this has more to do with convenience (for concrete-subclass authors) than with "guaranteeing correctness", since a given concrete subclass might bypass such mechanisms.
但这与方便性(对于具体子类作者)有关,而与“保证正确性”有关,因为给定的具体子类可能会绕过这些机制。
If you need to, you can offer a "debug/test mode" where properties are validated redundantly on access (probably not advisable in production use, but for debugging and testing it would help catch errant subclasses that are not properly calling validation methods). 如果需要,您可以提供“调试/测试模式”,其中属性在访问时被冗余验证(在生产使用中可能不可取,但是对于调试和测试,它将有助于捕获未正确调用验证方法的错误子类)。
The golden rule is to "encapsulate" property_b
so that the subclass provides part of the implementation, but not all of it. 黄金法则是“封装”
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
It's hard to say precisely what's supposed to happen when you have two classes and you're asking about allocation of responsibility. 当你有两个课程并且你在询问责任分配时,很难准确地说出应该发生什么。
It appears that you want the superclass to be responsible for some aspect of the relationship between a and b 看来您希望超类负责a和b之间关系的某些方面
It appears that you want the the subclass to be responsible for some other aspect of computing b, and not responsible for the relationship. 看来您希望子类负责计算b的某些其他方面,而不负责该关系。
If this is what you want, then your design must assign responsibility by decomposing things into the part the superclass is responsible for and the part the subclass is responsible for. 如果这是您想要的,那么您的设计必须通过将事物分解为超类负责的部分以及子类负责的部分来分配责任。
I suggest that you don't raise raise NotImplementedError
but call a method instead which raises this error. 我建议您不要引发
raise NotImplementedError
但是调用一个方法而不是引发此错误。 Subclasses then have to override that method (instead of property_b
). 然后子类必须覆盖该方法(而不是
property_b
)。 In property_b
, you call the method and then verify the result. 在
property_b
,您调用该方法,然后验证结果。
Rationale: You should check the value as soon as possible (which is when someone changes it). 理由:您应该尽快检查该值(即有人更改时)。 Otherwise, an illegal value could be set and cause a problem much later in the code when no one can say how it got there.
否则,可能会设置非法值,并且在没有人能够说明它是如何到达时会在代码中导致问题。
Alternatively, you could store the value and a stack trace. 或者,您可以存储值和堆栈跟踪。 When the value is used, you can then check the value and print the original stack trace as "value was changed here".
使用该值时,您可以检查该值并将原始堆栈跟踪打印为“此处更改了值”。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.