As an alternative to linked solution with disabling __setattr__
until after initialisation, you can wrap the attributes as property, writing them explicit setters that check the condition before assigning to actual attribute.
class Test:
def __init__(self, A, B, C):
self._A = A
self._B = B
self._C = C
def _check(self, **newValues):
d = {"A": self._A, "B": self._B, "C": self._C}
d.update(newValues)
if sum(d.values()) > 10:
raise ValueError
@property
def A(self):
return self._A
@A.setter
def A(self, value):
self._check(A=value)
self._A = value
@property
def B(self):
return self._B
@B.setter
def B(self, value):
self._check(B=value)
self._B = value
@property
def C(self):
return self._C
@C.setter
def C(self, value):
self._check(C=value)
self._C = value
def __str__(self):
return f'{self.A}, {self.B}, {self.C}'
t = Test(2,2,2)
t.B = 8 # ValueError
Also, with check
written the way you did in your question, you need to change the object before making the check. Which means if error is thrown, the object will still be modified to presumably incorrect value. With the way I implemented it in this answer, you can make check without changing the actual value, so in case of error the object remains unchanged.