Reputation: 11
I am trying to wrap my head around the different ways of using getters and setters through @property. I have read docs, blogs and posts here, but can't find a convention. There might not be one, but I assume some approaches are more suited for some applications and some are maybe more pythonic?
Any best practices?
Example 1 (I assume it is redundant to do the check for a<0 in setter and init?):
class ExponentialDecay:
def __init__(self, a: float) -> None:
if a < 0:
raise ValueError("Decay constant cannot be negative.")
self._a = a
@property
def decay(self) -> float:
return self._a
@decay.setter
def decay(self, value: float) -> None:
if value < 0:
raise ValueError("Decay constant cannot be negative.")
self._a = value
Example 2 (use setter in init):
class ExponentialDecay:
def __init__(self, a: float) -> None:
self.decay = a
@property
def decay(self) -> float:
return self._a
@decay.setter
def decay(self, value: float) -> None:
if value < 0:
raise ValueError("Decay constant cannot be negative.")
self._a = value
Example 3: Same code, but using name mangling in init or in the setter (self.__decay).
Trying to learn more, not stuck on a problem per se.
Upvotes: 0
Views: 73
Reputation: 530813
To some extent, _a
is an implementation detail of the property, not ExponentialDecay
, and so ExponentialDecay
should not use it directly, only "agree" to store it for the property to use. So Example 2 is "correct": ExponentialDecay.__init__
should let decay
check and store the value of a
itself.
(The name _a
and its existence, though, are considered part of the public interface of the property. Name-mangling would prevent subclasses of ExpoentialDecay
from overwriting it, but not from ExponentialDecay
itself from doing so. So we can thankfully dismiss Example 3 as a necessary option.)
It's worth noting that neither the getter or setter are methods of ExponentialDecay
, but simply functions stored by the property
object which the property calls using an ExponentialDecay
instance as an explicit argument. For example,
self.decay = a
is implemented with
type(self).decay.__set__(self, a)
which itself is implemented by
type(self).decay.fset(self, a)
Upvotes: 0