0

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?

1 Answers1

1

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 :)

h4z3
  • 5,265
  • 1
  • 15
  • 29
  • I see. This is a lot simpler, as well. Thank you. – newbiecoder11 Oct 20 '21 at 07:34
  • 1
    Beware. Slots are not intended to prevent adding new attributes to objects but are an optimization feature - read official Python doc about that point. Specifically subclasses will get a `__dict__` special attribute which will allow dynamic creation of new attributes. – Serge Ballesta Oct 20 '21 at 07:41
  • @SergeBallesta The doc link is already in my answer, I only quoted parts relevant to the question. :) But as to what you mention, from those same docs: `The action of a __slots__ declaration is not limited to the class where it is defined. __slots__ declared in parents are available in child classes. However, child subclasses will get a __dict__ and __weakref__ unless they also define __slots__ (which should only contain names of any additional slots).` – h4z3 Oct 20 '21 at 07:49