Constructing In Private @property • Python
Tags: python, codenewbie, pythonic, tutorial
Published: 2020-07-27 02:01:50

So you’ve mastered the basics of Python classes, and now you are trying to do something more intermediate. Maybe you come from a purely Object-Oriented Programming background like Java and have created some getters and setters on your class only to be told by your coworker that this isn't the "pythonic" way, whatever that means. Or you just realized that the completed project you've been handed needs a modification to a class attribute, but adding getter or setter method would mean changes to hundreds of lines of code and reworking the unit tests.

In this article, I will walk you through the process using the decorator @property to modify the getter, setter, and delete functions of an attribute in a python class. For this example, let’s assume that you have already declared this attribute in the __init__ method of your class:

```PYTHON
   class Classy:
       def __init__(self, var):
           self.var = var
```

Let's use the @property decorator to first create the getter. The getter function serves both as a getter and to define the name of the attribute for the decorator.

```PYTHON
class Classy:
    def __init__(self, var):
        self.var = var
 
    @propierty
    def var(self):
        return self._var
```

For this function to work, the name of the function must match the name of the attribute on our _init_ method. However, we return self._var. Why get a different attribute? After all, self._var is not the same as self.var. Let's break it down Barney style. If we try to return self.var, first, the program calls the getter of self.var. Then the getter calls the getter of self.var, which in turn calls the getter of self.var. Then the program finds itself in recursion hell. 

"But if I use self._var, my IDE yells at me," you may say. 

"Where is this self._var you speak of? I've never heard of this self._var chap!" the helpful IDE points out in a posh British accent. 

Let's help quiet your IDE by creating a setter for the attribute. Now that we created and named our getter function of the @property, we can use the pattern @attribute_name.function to define the other functions for this attribute.

```PYTHON
class Classy:
    def __init__(self, var):
        self.var = var
 
    @propierty
    def var(self):
        return self._var
 
    @var.setter
    def var(self, value):
        self._var = value
```

Once again, we match the name of the function with the name of the attribute in our _init_ method. Then we save the value into a "hidden/private" variable we have named self._var, thus avoiding the recursive armageddon and quieting your py IDE's charming warning of impending doom. 

Likewise, we may add a deleter function following the same system.

```PYTHON
class Classy:
    def __init__(self, var):
        self.var = var
 
    @propierty
    def var(self):
        return self._var
 
    @var.setter
    def var(self, value):
        self._var = value

    @var.deleter
    def var(self):
        self._var = None
```

Using the @property decorator is a simple way to extend the getter/setter functionality of your python class. It makes your code more resilient and streamlines the process of attribute modification and retrieval. And yes, it makes your code more “pythonic.”  So, in conclusion, here are the most important properties to remember when using a @property decorator:

- The getter will serve both to retrieve and name the attribute in question. There is no such thing as a `@var.getter`.
- The name of the function(s) must match the name of the attribute it modifies. 
- To avoid the dreaded "recursion limit error," the attribute's value must be stored in a new "hidden/private" variable.