0

How do I make the check run after every attribute update?

class Test:
    def __init__(self, A, B, C):
        self.A = A
        self.B = B
        self.C = C
        self.check()

    def check(self):
        if self.A + self.B + self.C > 10:
            raise ValueError

    def __str__(self):
        return f'{self.A}, {self.B}, {self.C}'


>> t = Test(2, 2, 2)
>> t.A = 8
>> print(t)
>> 8, 2, 2

I tried to do it with setattr but then the check will execute when not all attributes are assigned to the object yet

biboch
  • 3
  • 1
  • 1
    You can take a look at this: [Clean way to disable `__setattr__` until after initialization](https://stackoverflow.com/questions/12998926/) – Jorge Luis Apr 13 '23 at 13:31

1 Answers1

1

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.

matszwecja
  • 6,357
  • 2
  • 10
  • 17
  • 1
    @JorgeLuis It takes into account new value before changing attribute, which means if error is raised, the object remains unchanged. – matszwecja Apr 13 '23 at 13:49