Descriptor to restrict input We start off we the following Person class:

class Person:
    def __init__(self):
        self.moods = {
            "happy": 5,
            "angry": 5,
            "sad": 5
        }

    def updateStates(self, stateUpdates: {}):
        for mood, valChange in stateUpdates.items():
            self.moods[mood] += valChange


From here I implemented a restriction on what the values self.moods can be.
I have implemented the restriction using descriptors. But I find it distasteful.

The following works. But I don't think it is worth it because now I have to append .__get__(instance=self, owner=None) to self.moods[&lt;mood&gt;] whenever I need to access a mood, or .__set__(instance=self, value=&lt;newVal&gt;) whenever I want to update one. This will quickly pollute my code and make it unreadable, which is why I am not a fan of this approach.

class OneDigitNumericValue():    # descriptor class
    def __set_name__(self, owner, name):
        self.name = name

    def __init__(self, defaultVal=5):
        self.value = defaultVal

    def __get__(self, instance, owner) -&gt; object:
        return self.value

    def __set__(self, instance, value) -&gt; None:
        if not (0 &lt; value &lt; 9) or int(value) != int:
            raise AttributeError("The value is invalid")
        self.value = value

class Person:
    moods = {
        "happy": OneDigitNumericValue(),
        "angry": OneDigitNumericValue(),
        "sad": OneDigitNumericValue()
    }

    def updateStates(self, stateUpdates: {}):
        for mood, valChange in stateUpdates.items():
            oldVal = self.moods[mood].__get__(self, owner=None)
            self.moods[mood].__set__(self, value=oldVal + valChange)
Your solution just doesn't work


Your validation check always fails. Yes int(value) != int is always true. I don't think an instance of a class will ever be the class that it's an instance of.
Your mutating a class variable. The tool you're utilizing forces you to take instances, normally there's a pretty good reason.

If we remove the previous error and run your code we can easily see, there's no point in having more than one instance of a Player.

&gt;&gt;&gt; a = Person()
&gt;&gt;&gt; b = Person()
&gt;&gt;&gt; a.moods['happy'].__set__(a, 8)
&gt;&gt;&gt; b.moods['happy'].__get__(b, None)
8  # Ok, that's not right...



I don't believe you've actually tried to code you've provided. And honestly what's the point in a single-data non-singleton class? The provided code is just untested garbage.

You're using it wrong

If it's not evident enough that you're fighting a tool that is pure sugar. Then you're not doing it right. The instance has to keep the state of the instance, and the descriptor has to interact with a varying amount of instances to interact with their state correctly.

If anything of the instance's leaks out into the descriptor, or vice versa. Then you're going to have a bad, bad time.

Since descriptors are to be tied onto classes I'm making a Mood class. Since the descriptor needs to have a place to store data. We can define that in the __init__ of Mood. To note, because you may not notice this by yourself. self._values can be mutated to bypass the validator. Using self._values[mood] = value is not how you update values, use setattr(self, mood, value).

In addition to fixing OneDigitNumericValue, you should make it take an argument that is the validator for the function. If you need another validator it'd be better and simpler to make a simple hard to mess-up function. Rather than making lots of potentially broken descriptors.

def single_digit(value):
    if 0 &lt;= value &lt;= 9:
        return
    raise ValueError("The value is invalid")


class Validator:
    def __init__(self, validator):
        self.validator = validator
        self.name = None

    def __set_name__(self, owner, name):
        self.name = name

    def __get__(self, instance, owner) -&gt; object:
        return instance._values[self.name]

    def __set__(self, instance, value) -&gt; None:
        self.validator(value)
        instance._values[self.name] = value


class Mood:
    happy = Validator(single_digit)
    angry = Validator(single_digit)
    sad = Validator(single_digit)

    def __init__(self):
        self._values = {}
        self.happy = 5
        self.angry = 5
        self.sad = 5

    def update_states(self, states):
        for mood, value in states.items():
            setattr(self, mood, value + getattr(self, mood))


And it actually works:

&gt;&gt;&gt; a = Mood()
&gt;&gt;&gt; b = Mood()
&gt;&gt;&gt; a.happy = 3
&gt;&gt;&gt; a.happy
3
&gt;&gt;&gt; b.happy
5
&gt;&gt;&gt; a.update_states({'happy': 3})
&gt;&gt;&gt; a.happy
6