2

Suppose that we had a class that at a certain point after creation will need to get assigned a value:

class ProcStatus:
    def __init__(self, name):
        self.name = name
        self._status_code = None

    def set_status_code(self, value):
        self._status_code = value

    def print_proc_status(self):  # Some function that does something with the status code
        print(f'{self.name} returned code {self._status_code}')


status = ProcStatus('ls')
# Some code in between
status.set_status_code(1)
status.print_proc_status()

Using this design, it's clear by the interface that the caller can (and is encouraged to) explicitly set the status code. However, the more pythonic approach would be using an attribute:

class ProcStatus:
    def __init__(self, name):
        self.name = name
        self.status_code = None

    def print_proc_status(self):  # Some function that does something with the status code
        print(f'{self.name} returned code {self.status_code}')


status = ProcStatus('ls')
# Some code in between
status.status_code = 1
status.print_proc_status()

Is the second one a better design, despite looking a little misleading since status_code is never updated within the class? Alternatively, would a property setter and getter assigning to a private field be the right approach?

Matthew D. Scholefield
  • 2,977
  • 3
  • 31
  • 42
  • 1
    Making `status_code` a public attribute makes it even clearer that the caller can set its value. Don't bother with a setter method unless you want to constrain what values can be assigned to it. The nice thing about properties is that you can replace a public attribute with a property, if needed, without changing the interface. – chepner Aug 02 '19 at 15:54

2 Answers2

1

The utility of having setters and getters for a variable is that they may enforce some constraints or maintain class invariants on user-set values to the field - in other words, they allow you to "sanitize" an otherwise potentially arbitrary value.

The most pythonic way to achieve the best of both worlds (your example #1 and #2) would be to use the @property decorator, which would look something like:

class ProcStatus:
    def __init__(self, name):
        self.name = name
        self._status_code = None

    @property
    def status_code(self):
        print("Getting")
        return self._status_code

    @property.setter
    def status_code(self, value):
        print("Setting")
        # maybe apply constraints, check `value` for validity
        self._status_code = value

Now you have the additional benefit of setters and getters and the beauty of status.status_code = 1 or print(status.status_code)

Skrino
  • 520
  • 2
  • 12
  • Provided that `status_code` does not need to be checked for validity, is this still the right approach? Because in that case it seems redundant to have a setter and getter that merely set and get a private attribute. – Matthew D. Scholefield Aug 02 '19 at 15:56
  • It's a fine balance that the implementer has to strike. On the one hand, you are right, adding the boilerplate of properties to a simple (i.e. unconstrained) field is verbose. Yet on the other hand, it explicitly communicates the intention that this field is public and to be interacted with by the user of the class, whereas without the boilerplate the user has to either read the documentation, if it exists, to pick out the relevant class field or to infer from the implementation that it is user-facing. – Skrino Aug 02 '19 at 16:04
  • Additionally, note that the convention of an underscore suffix to a variable name immediately communicates that the field _is not_ to be interacted with by the class user and should not be accessed in any way on an object of the class (e.g. `status.status_code_ = 1` screams illegal access). – Skrino Aug 02 '19 at 16:07
  • When you use a class, it is in general for the methods that it provides so the most important is that the methods explain what they do in their documentation. Documentation is very important and you shouldn't act like it is a downside. Whether a variable needs to be changed depends on when its used, not whether there exists methods to change it or not. – Akaisteph7 Aug 02 '19 at 16:29
1

I think, in the end, this really depends on your situation. There are two scenarios:


#1 - Not using getters/setters (preferred)

In terms of using a getter/setter (Java style) vs an attribute, an attribute is always preferred as the more pythonic way of doing this. Whenever the user needs the status_code to have a value, they can just overwrite the attribute's value with a simple assignment operation:

status.status_code = 1

What is important is that the user understands how your class works and what fields are important for what. If status_code is needed for a function, you should either request it from the user as a parameter or let them know in the documentation that the function depends on a previously set status_code value.


#2 - When to use getters/setters

The only reason you would want a getter/setter type scenario is when you want to format the output created from accessing your attribute and to check the value's of what is assigned to it (_status_code in this case). So, basically, when you don't want your user to directly interact with the variable. This can be done the pythonic way and here I think Skrino's answer comes into play.

In terms of calling it a "private" variable, python does not really have any of those. This SO post goes into length about this. But in these cases, when you want to let your user be able to change a variable's value with some constraints, then using a getter/setter with a "private" variable is a good idea.

Akaisteph7
  • 5,034
  • 2
  • 20
  • 43