2

I'm following this code example from a python course:

class P:

    def __init__(self,x):
        self.x = x

    @property
    def x(self):
        return self.__x

    @x.setter
    def x(self, x):
        if x < 0:
            self.__x = 0
        elif x > 1000:
            self.__x = 1000
        else:
            self.__x = x

And I tried to implement this pattern to my own code:

class PCAModel(object):
    def __init__(self):
        self.M_inv = None

    @property
    def M_inv(self):
        return self.__M_inv

    @M_inv.setter
    def set_M_inv(self):
        M = self.var * np.eye(self.W.shape[1]) + np.matmul(self.W.T, self.W)
        self.__M_inv = np.linalg.inv(M)

Note that I want the M_inv property to be None before I have run the setter the first time. Also, the setter solely relies on other properties of the class object, and not on input arguments.

The setter decorator generates an error:

NameError: name 'M_inv' is not defined

Why is this?

Sahand
  • 7,980
  • 23
  • 69
  • 137
  • 3
    rename def set_M_inv to def M_inv. It should match the decorator above it. https://stackoverflow.com/questions/598077/why-does-foo-setter-in-python-not-work-for-me –  Jan 08 '19 at 13:42
  • Thanks, it solved the issue! – Sahand Jan 08 '19 at 13:43

2 Answers2

1

Your setter method should be like below:

@M_inv.setter
def M_inv(self):
    M = self.var * np.eye(self.W.shape[1]) + np.matmul(self.W.T, self.W)
    self.__M_inv = np.linalg.inv(M)

The decorator @M_inv.setter and the function def M_inv(self): name should be same

ansu5555
  • 416
  • 2
  • 7
0

The example is wrong.

EDIT: Example was using a setter in __init__ on purpose.

Getters and setters, even though they act like properties, are just methods that access a private attribute. That attribute must exist. In the example, self.__x is never created.

Here is my suggested use :

class PCAModel(object):
    def __init__(self):
        # We create a private variable
        self.__M_inv = None

    @property
    def M_inv(self):
        # Accessing M_inv returns the value of the previously created variable
        return self.__M_inv

    @M_inv.setter
    def M_inv(self):  # Keep the same name than your propery
        M = self.var * np.eye(self.W.shape[1]) + np.matmul(self.W.T, self.W)
        self.__M_inv = np.linalg.inv(M)
Rémi Héneault
  • 383
  • 3
  • 12
  • isn't it the case in the example that the setter is implicitly used in the `__init__` function? – Sahand Jan 08 '19 at 14:05
  • Indeed, I didn't see it this way. Is there a reason to use a setter for initializing the variable? – Rémi Héneault Jan 08 '19 at 14:19
  • I would personally stick to using getters and setters **outside** the `__init__` method. It makes it hard to follow otherwise. – Rémi Héneault Jan 08 '19 at 14:26
  • 1
    There's no problem with using the setter inside `__init__`; in fact, *not* using it means you either need to duplicate the bounds check in `__init__`, or `__init__` can violate whatever constraint the setter is supposed to enforce. – chepner Jan 08 '19 at 14:31
  • You seem right, point noted. However one should be fine initializing the private variable to `None`, and it allows better comprehension on first read. – Rémi Héneault Jan 08 '19 at 14:46
  • The setter *creates* the attribute; there is no rule that every attribute has to be initialized in `__init__`. It's a good idea for most attributes, but the ones governed by properties can be initialized in `__init__` by using the setter. – chepner Jan 08 '19 at 14:58