outon
outon

Reputation: 116

Is it possible to call getter and setter properties with parameters in Python?

Probably this can't be done the way I would like. I was making a class Temperature in python.

I would like to set a property called temperature and set or retrieve its value on different scales like Kevin, Celsius or Fahrenheit.

Set temperature:

T = Temperature() 
T.temperature = 20                # Set temperature to 20ºC
T.temperature('Fahrenheit') = 68  # Set temperature to 68ºF
T.temperature('Kelvin') = 305     # Set temperature to 305K

Get temperature:

t = T.temperature                 # retrieves temperature in Celsius
t = T.temperature('Fahrenheit')   # retrieves temperature in Fahrenheit
t = T.temperature('Kelvin')       # retrieves temperature in Kelvin

I have this code. Firstly I tried to use @property decorator but it was not working. Then I decided to move to traditional syntax using x=property(x_get, x_set)

class Temperature:
    def __init__(self, temperature:float = 0, scale:str = "Celsius"):
        # All temperatures will be stored as KELVIN
        self._temperature = None
        self.set_temperature(temperature, scale)

    def get_temperature(self, scale: str = "Celsius"):
        converter = {
            "Celsius": self._temperature - 273.15,
            "Fahrenheit": ((self._temperature - 273.15) * 1.8) + 32,
            "Kelvin": self._temperature
        }
        value = converter.get(scale)

        return value

    def set_temperature(self, value: float, scale: str = "Celsius"):
        converter = {
            "Celsius": value + 273.15,
            "Fahrenheit": (value - 32) / 1.8 + 273.15,
            "Kelvin": value
        }

        self._temperature = converter.get(scale)

        if self._temperature < 0 :
            self._temperature = None
            raise ValueError("Temperature below 0 Kelvin is not possible")

    temperature = property(get_temperature, set_temperature)

But I cannot use the proposed syntax; although using traditional syntax seems to be a good approach.

It works when calling to property temperature without any parameters or directly to get and set methods with parameters:

>>> T = Temperature(20) # Initialize
>>> T.temperature       # Getter
20.0

>>> T.temperature=30    # Setter
>>> T.temperature       # Getter
30.0

It works if I call directly to get_temperature or set_temperature

>>> T.get_temperature("Fahrenheit")
86.0

But if I call to getter or setter property with parameter it raises an error:

T.temperature("Fahrenheit")
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: 'float' object is not callable

Or

T.temperature("Kelvin") = 301.15
  File "<input>", line 1
SyntaxError: can't assign to function call


T.temperature = 68, 'Fahrenheit'
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<input>", line 19, in set_temperature
TypeError: can only concatenate tuple (not "float") to tuple

Upvotes: 1

Views: 894

Answers (1)

Taylr Cawte
Taylr Cawte

Reputation: 612

I don't think getters and setters are necessary, nor pythonic... By using getters and setters you are making attributes of a class private to protect them from other codes. The pythonic way of providing this protection is actually by introducing the attributes publicly.

to design a class in a psuedo-java way you could do the following:

class temperature:

    def __init__(self.temp):
        self.__temp = temp
    
    def get_temp(self): 
        return self.__temp
   
    def set_temp(self, temp):
        self.__temp = temp

and to access the data you would do the below

>>>temp1 = temperature(135)
>>>temp1.set_temp(14)
>>>temp1.get_temp()
14

however, a more pythonic way to design this class would be without the getter and setter and using a public attribute.

class temperature: 

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

Now you can just access the same attributes without having to use getters or setters

>>>temp1 = temperature(14)
>>>temp1.temp
14

Even though data is not necessarily encapsulated in this way ... it doesn't really need to be.

I realize that this doesn't address your concern with the three temperature scales but this can easily be accounted for by specifying the input temperature to a certain unit and then using conversion etc....

EDIT

On comment from @Carlo

The pythonic temperature class could be done below, assuming you specify an input temperature unit of kelvin, otherwise this class would need to be expanded to included farenheit and celsius input temperatures

class temperature():

    def __init__(self, temp, unit):
        self.temp = temp
        self.unit = unit

        if self.unit == 'K':
            self.temp_k = self.temp
            self.temp_c = self.temp_k + 273
            self.temp_f = (self.temp_k + 273)*(9/5)+32
        else:
            raise TypeError("only kelvin accepted as input temperature")


if __name__ == '__main__':

    temp1 = temperature(14, 'K')
    temp2 = temperature(14, 'C')  # will raise error

Accessing the temperature values for the temp1 object then simply becomes

>>> temp1.temp_k
14
>>> temp1.temp_c
287
>>> temp1.temp_f
548.6

Upvotes: 1

Related Questions