Naga
Naga

Reputation: 393

Using python class variable vs instance variable

I am new to python. I'm trying to create a configuration class with required validators. In below code snippet, accessing variable 'a' using python class and instance of class is returning a different value. Whether this is a proper design or should i initialise var 'a' only in the class constructor and do validation in the setter method.

class IntField:
    def __get__(self, instance, owner):
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        if not isinstance(value, int):
            raise ValueError('expecting integer')
        instance.__dict__[self.name] = value

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

class DefaultConfig:
    a = IntField()

    def __init__(self):
        self.a = 2

print(DefaultConfig.a)
print(DefaultConfig().a)

output:

<__main__.IntField object at 0x10c34b550>
2

Upvotes: 2

Views: 108

Answers (1)

Serge Ballesta
Serge Ballesta

Reputation: 149155

I just want to know whether it's right way to do it

Rather asking for opinion only answer, by I will try to be as objective as I can.

Your code behaves as expected as long as instances attributes are processed:

>>> c = DefaultConfig()
>>> c.a = 'foo'
Traceback (most recent call last):
  File "<pyshell#88>", line 1, in <module>
    c.a = 'foo'
  File "<pyshell#83>", line 10, in __set__
    raise ValueError('expecting integer')
ValueError: expecting integer
>>> c.a = 4
>>> c.a
4

When inspecting DefaultConfig.a, the __get__ function is still used with instance=None. So you can choose one of 2 possible ways:

  • be transparent and show what the attribute actually is (what you currently do)
  • insist on the descriptor magic and return the default value (here 2).

For that latter way, code could become:

class IntField:
    def __get__(self, instance, owner):
        if instance is None:
            return getattr(owner, '_default_' + self.name, self)
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        if not isinstance(value, int):
            raise ValueError('expecting integer')
        instance.__dict__[self.name] = value

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

class DefaultConfig:
    a = IntField()
    _default_a = 2
    def __init__(self):
        self.a = self._default_a

The trick here is that by convention, the default value for an attribute x is expected to be _default_x.

In that case, you will get:

print(DefaultConfig.a)
2

Upvotes: 1

Related Questions