Search code examples
pythonclasssetattr

__setattr__ to prohibit changes to instance/self variables?


I have a class Fraction whose init creates the instance variables self.num = num and self.denom=denom:

    def __init__(self,num=0,denom=1):
        assert isinstance(num, int), "Numerator and denominator must be an integer"
        assert isinstance(denom, int), "Numerator and denominator must be an integer"
        assert denom != 0, "Denominator cannot be 0."
        self.num = num
        self.denom = denom

and I'm trying to write its __setattr__ method to prohibit changes to the instance variables once they have been initialized by raising a NameError:

    def __setattr__(self,name,value):
        if name not in Fraction.__dict__:
            self.__dict__[name] = value
        else:
            raise NameError

From printing Fraction.__dict__, I can see that the dict contains Fractions methods, rather than num,denom and their respective values. So I then tried to do: if name not in Fraction().__dict__: and I ended up with a infinite recursion error. How can I access the dictionary that contains the instance variables?


Solution

  • You should use __slots__ instead to limit attributes to only those you want.

    https://docs.python.org/3/reference/datamodel.html#slots

    __slots__ allow us to explicitly declare data members (like properties) and deny the creation of __dict__ and __weakref__ (unless explicitly declared in __slots__ or available in a parent.)

    [...] Without a __dict__ variable, instances cannot be assigned new variables not listed in the __slots__ definition. Attempts to assign to an unlisted variable name raises AttributeError. [...]

    So basically in your class add this, preferably somewhere at the top (just after class line and before init):

    __slots__ = 'num', 'denom'
    

    And remove your setattr :)