As a simple example, take a class
Ellipse that can return its properties such as area A
, circumference C
, major/minor axis a/b
I would check for the consistency of the data each time you set a parameter.
import math
tol = 1e-9
class Ellipse(object):
def __init__(self, a=None, b=None, A=None, a_b=None):
self.a = self.b = self.A = self.a_b = None
self.set_short_axis(a)
self.set_long_axis(b)
self.set_area(A)
self.set_maj_min_axis(a_b)
def set_short_axis(self, a):
self.a = a
self.check()
def set_long_axis(self, b):
self.b = b
self.check()
def set_maj_min_axis(self, a_b):
self.a_b = a_b
self.check()
def set_area(self, A):
self.A = A
self.check()
def check(self):
if self.a and self.b and self.A:
if not math.fabs(self.A - self.a * self.b * math.pi) <= tol:
raise Exception('A=a*b*pi does not check!')
if self.a and self.b and self.a_b:
if not math.fabs(self.a / float(self.b) - self.a_b) <= tol:
raise Exception('a_b=a/b does not check!')
The main:
e1 = Ellipse(a=3, b=3, a_b=1)
e2 = Ellipse(a=3, b=3, A=27)
The first ellipse object is consistent; set_maj_min_axis(1)
passes fine.
The second is not; set_area(27)
fails, at least within the 1e-9 tolerance specified, and raises an error.
Edit 1
Some additional lines are needed for the cases when the uses supply a
, a_b
and A
, in the check()
method:
if self.a and self.A and self.a_b:
if not math.fabs(self.A - self.a **2 / self.a_b * math.pi) <= tol:
raise Exception('A=a*a/a_b*pi does not check!')
if self.b and self.A and self.a_b:
if not math.fabs(self.A - self.b **2 * self.a_b * math.pi) <= tol:
raise Exception('A=b*b*a_b*pi does not check!')
Main:
e3 = Ellipse(b=3.0, a_b=1.0, A=27)
An arguably wiser way would be to calculate self.b = self.a / float(self.a_b)
directly into the set method of a_b
. Since you decide yourself of the order of the set methods in the constructor, that might be more manageable than to write dozens of checks.