Problem statement:
Write a program that converts all given temperatures from a given input temperature scale to a given output temperature scale. The temperature scales to be supported are Kelvin, Celsius, Fahrenheit, Rankine, Delisle, Newton, Rømer, Réaumur.
Synopsis: tempconv INPUT_SCALE OUTPUT_SCALE [TEMPERATURE]...
The INPUT_SCALE
and OUTPUT_SCALE
shall be given as follows:
K
for KelvinC
for CelsiusF
for FahrenheitR
for RankineD
for DelisleN
for NewtonRø
for RømerRé
for Réaumur.
Example:
tempconv K C 0 273.15 373.15
-273.15
0.0
100.0
My solution in Python:
#!/usr/bin/python3
import sys
from typing import Callable
from dataclasses import dataclass
@dataclass(frozen=True)
class TemperatureConverter:
to_kelvin: Callable[[float], float]
from_kelvin: Callable[[float], float]
names: list[str]
class TemperatureConverters:
CELSIUS = TemperatureConverter(lambda celsius: celsius + 273.15, lambda kelvin: kelvin - 273.15, ["°C", "C", "c"])
KELVIN = TemperatureConverter(lambda kelvin: kelvin, lambda kelvin: kelvin, ["K", "k"])
FAHRENHEIT = TemperatureConverter(lambda fahrenheit: (fahrenheit + 459.67) * 5 / 9, lambda kelvin: kelvin * 9 / 5 - 459.67, ["°F", "F", "f"]),
RANKINE = TemperatureConverter(lambda rankine: rankine * 5 / 9, lambda kelvin: kelvin * 9 / 5, ["°R", "R", "r"]),
DELISLE = TemperatureConverter(lambda delisle: 373.15 - delisle * 2 / 3, lambda kelvin: (373.15 - kelvin) * 3 / 2, ["°De", "De", "DE", "de"]),
NEWTON = TemperatureConverter(lambda newton: newton * 100 / 33 + 273.15, lambda kelvin: (kelvin - 273.15) * 33 / 100, ["°N", "N", "n"]),
RÉAUMUR = TemperatureConverter(lambda réaumur: réaumur * 5 / 4 + 273.15, lambda kelvin: (kelvin - 273.15) * 4 / 5, ["°Ré", "°Re", "Ré", "RÉ", "ré", "Re", "RE", "re"]),
RØMER = TemperatureConverter(lambda rømer: (rømer - 7.5) * 40 / 21 + 273.15, lambda kelvin: (kelvin - 273.15) * 21 / 40 + 7.5, ["°Rø", "°Ro", "Rø", "RØ", "rø", "Ro", "RO", "ro"]),
@classmethod
def get(cls, name: str):
for prop, val in vars(cls).items():
if isinstance(val, TemperatureConverter):
if name in val.names:
return val
inputConverter = TemperatureConverters.get(sys.argv[1]).to_kelvin
outputConverter = TemperatureConverters.get(sys.argv[2]).from_kelvin
for arg in sys.argv[3:]:
print(outputConverter(inputConverter(float(arg))))
I know that the lines defining the conversions are a bit long, I'm okay with sacrificing line length to retain a readable tabular format in such a case.
I'm looking for more concise ways, for example, is there a way to turn the @classmethod get()
into an operator, like in Kotlin? I tried to change it to __getitem__
, but Python didn't like that for a @classmethod
. Also, is there a smart way to join TemperatureConverter
and TemperatureConverters
into one class? I tried, and I found that difficult to do without losing the conciseness from @dataclass(frozen=True)
.