3

I'm trying to understand how getters and setters work in python with properties, but I'm confused in the naming convention.

Is there a specific naming standard for the getter and setter? Why do I need to set the returning name of parameter with an "_" (underscore)? If I were to change it to just "self.color" instead of "self._color", the getter and setter no longer work. Also, if I were to change the name of the functions the print function is no longer is executed. Any clarification would be greatly appreciated, thanks!

class FavoriteColor:
    def __init__(self, color):
        self.color = color

    @property
    def color(self):
        print('getter method')
        return self._color

    @color.setter
    def color(self, x):
        print('setter method')
        self._color = x

obj1 = FavoriteColor('blue')

dko512
  • 411
  • 4
  • 15

4 Answers4

3

If you did this:

@color.setter
def color(self, x):
    self.color = x

Then this setter would call itself repeatedly. There's no difference between obj1.color = 'blue' and self.color = x, they both do the same thing, which is to invoke the setter def color. So with the setter invoking itself recursively, you have an endless recursive loop which will eventually crash your program.

For that purpose you need to actually store the value on some other attribute. Using "_color" for its name is just the rather obvious solution.

Note that using this kind of setter/getter in Python is frowned upon, if your setter/getter doesn't do anything. You can remove them entirely and just plainly set self.color = 'blue' for exactly the same effect as your setter/getter pair currently has. You should only use setters/getters if they do some additional processing. Since the syntax of a plain attribute and a setter/getter are identical, you can even safely transition to using setters/getters later on if you need to (in contrast to, say, Java, where a setter/getter is foo.setBar(...), which is not identical to foo.bar = ... and cannot be transparently substituted later).

deceze
  • 510,633
  • 85
  • 743
  • 889
  • `you can even safely transition to using setters/getters later on if you need to `: googlers, more info + code examples: https://stackoverflow.com/questions/2627002/whats-the-pythonic-way-to-use-getters-and-setters; tldr use `@property` right before `def` – ᴍᴇʜᴏᴠ Feb 13 '21 at 17:33
2

I guess the confusing part is in the __init__. The self.color = color in __init__ is actually calling your setter, which creates the private variable called _color. The setter and getter work as wrappers of this private _color allowing you to do something extra than accessing the raw variable directly.

I don't know if this is a common practice to call setter in __init__, but I prefer to defining the wrapped private variable in __init__ directly. To me it looks more straightforward

class FavoriteColor:
    def __init__(self, color):
        self._color = color
yyforbidden
  • 159
  • 8
1

Like most of the programming languages, Python has convention to define private variables of a class with an underscore "_", so by naming "_color" we are meant to create a private attribute 'color'.

And for your second question, you are meant to define getter setter property for the attribute that you just defined, According to rules of property decorator, we must have to define same name of getter setter methods as that of our targeted attribute along with @property decorator.

Hope it helps

class FavoriteColor:
     def __init__(self, color): 
          self._color = color

     # a getter funcion
     @property
     def color(self): 
         return self._color 

     # a setter function 
     @color.setter 
     def color(self, value): 
        self._color = value
0

Python variables and functions share the same namespace. Thus self.color can only refer to either the getter/setter method, or the attribute itself. A common convention is to add an underscore to the internal attribute name, to avoid this naming clash, which also conventionally conveys the notion that it is a private attribute.

This should already explain your other question. If you have a method named self.color and it contains code where it replaces itself with a value, then of course you can no longer use self.color to refer to the method again.

Let's look at that again with an even simpler example.

>>> class X:
...   def color (self, value):
...     self.color = value

The first time you call the function - say, xinstance.color("red") - it will set the instance's color to "red". So then you can no longer call xinstance.color() again because it is now a string, not a function.

Adding the @property decorator changes this somewhat, because now self.color = value will cause color to call itself, which causes it to call itself, which causes it to call itself again, etc.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • The setter wouldn't replace itself, it would just call itself recursively. And your `def` example also wouldn't replace `def color`, it just creates a local variable `color`. – deceze Feb 14 '20 at 07:53
  • @deceze Thanks for feedback -- updated with an actually tested example (-: – tripleee Feb 14 '20 at 07:57
  • Better, but the second paragraph still suggests that the setter is replacing itself, which it isn't. – deceze Feb 14 '20 at 07:59