Search code examples
pythonpropertiesdecoratorpython-internals

When using property decorator, should the variable be declared as public or private in __init__?


Let's consider the two implementations :

class A:
    def __init__(self, myvar):
        self._myvar = myvar # 

    @property
    def myvar(self):
        print("GET")
        return self._myvar

    @myvar.setter
    def myvar(self, newvar):
        print("SET")
        self._myvar = newvar

and

class B:
    def __init__(self, myvar):
        self.myvar = myvar

    @property
    def myvar(self):
        print("GET")
        return self._myvar

    @myvar.setter
    def myvar(self, newvar):
        print("SET")
        self._myvar = newvar

The only difference is in the __init__ where we initialised _myvar in class A and myvar in class B

a = A(myvar=5)
print(a.myvar)

will output

GET
5

and

b = B(myvar=5)
print(b.myvar)

will output

SET
GET
5

All in all, from my understanding, the only difference in the two implementations is in the init. An instance of class A will set _myvar directly while an instance of class B will go through myvar.setter to set _myvar. So my question is the following : Which implementation is considered more pythonic ? Is there a reason to use one over the other ?


Solution

  • class B is better way of doing.

    Lets say you have some validation steps in setter, for example you want to make sure only values in certain range are set to myvar. class B will make sure that the validation steps are run even via constructor. However in case of class A, you are directly updating the variable and in this case the validation steps sitting inside the setter method are skipped.

    class EvenOnly_A:
      def __init__(self, myvar):
          self._myvar = myvar # 
    
      @property
      def myvar(self):
          return self._myvar
    
      @myvar.setter
      def myvar(self, newvar):
          assert newvar%2 == 0
          self._myvar = newvar
    
    
    class EvenOnly_B:
      def __init__(self, myvar):
          self.myvar = myvar
    
      @property
      def myvar(self):
          return self._myvar
    
      @myvar.setter
      def myvar(self, newvar):
          assert newvar%2 == 0
          self._myvar = newvar 
    
    a = EvenOnly_A(3)
    b = EvenOnly_B(3)