ToTemp is a temperature conversion package with Celsius, Delisle, Fahrenheit, Kelvin, Rankine, Réaumur, Newton and Rømer scales. With a documentation and already in PyPI.
The source code for the main implementation here, since is too long to be completely shown, it is basically a ABC for Abstract Temperature Scales, which all scales inhehit from.
Here's a snippet of it (ommited most methods and docs for brevity, besides for __init_subclass__
, __init__
, __repr__
, __str__
, __add__
, convert_to
and to_fahrenheit
):
from __future__ import annotations
from abc import ABCMeta, abstractmethod
from typing import Any, ClassVar, TypeVar
T = TypeVar('T', bound='AbstractTemperature')
class AbstractTemperature(metaclass=ABCMeta):
"""
...
"""
_symbol: ClassVar[str]
_value: float
@classmethod
def __init_subclass__(cls, **kwargs: object) -> None:
"""Ensures subclasses set the `_symbol` attribute."""
super().__init_subclass__(**kwargs)
try:
_ = cls._symbol
except AttributeError:
raise AttributeError(
'Temperature subclasses must set the `_symbol` class attribute'
) from None
def __init__(self, value: float) -> None:
self._value = value
def __str__(self) -> str:
"""
...
"""
return f'{self._value} {self._symbol}'
def __repr__(self) -> str:
"""
...
"""
return f'{self.__class__.__name__}({self._value})'
def __add__(self: T, other: Any) -> T:
"""
Returns a new instance of the same class with the sum of the values.
If `other` is a temperature instance, it is first converted to the
calling class, then the values are added.
Otherwise, an attempt is made to add `other` to the value directly.
Notes
-----
If `other` is not a temperature instance, atempts to return: cls(self._value + other)
Returns
-------
self.__add__(other) : T
cls(self._value + other.convert_to(cls).value)
"""
cls = self.__class__
try:
if isinstance(other, AbstractTemperature):
return cls(self._value + other.convert_to(cls).value)
return cls(self._value + other)
except TypeError:
return NotImplemented
def to_fahrenheit(self) -> Fahrenheit:
"""
Returns a Fahrenheit object which contains the class attribute "value"
with the result from the conversion typed the same as the attribute.
Returns
-------
convert_to(Fahrenheit) : Fahrenheit
"""
return self.convert_to(Fahrenheit)
@abstractmethod
def convert_to(self, temp_cls: type[T]) -> T:
"""
Returns an instance of `temp_cls` containing the converted value.
If no conversion to `temp_cls` is possible, `TypeError` is raised.
"""
...
This package aims to bring the simple and straight to the point, but precise, Object Oriented experience of working with temperature scale data types.
First of all, install the package:
pip install totemp
The instances:
from totemp import Celsius, Fahrenheit
if __name__ == '__main__':
temps: list = [Celsius(12), Celsius(25), Celsius(50)]
print(temps[0]) # '12 ºC'
print(temps) # [Celsius(12), Celsius(25), Celsius(50)]
temps = list(map(Celsius.to_fahrenheit, temps))
print(temps[0]) # '53.6 ºF'
print(temps) # [Fahrenheit(53.6), Fahrenheit(77.0), Fahrenheit(122.0)]
It's representations and properties:
Property
symbol
is read-only.
from totemp import Fahrenheit
if __name__ == '__main__':
temp0 = Fahrenheit(53.6)
print(temp0.__repr__()) # 'Fahrenheit(53.6)'
print(temp0.__str__()) # '53.6 ºF'
print(temp0.symbol) # 'ºF'
print(temp0.value) # 53.6
Comparision operations ('==', '!=', '>', '>=', '<',...):
The comparision/arithmetic implementation attempts to convert the value of
other
(if it is a temperature instance) and then evaluate the expression.
import totemp as tp
if __name__ == '__main__':
temp0, temp1 = tp.Celsius(0), tp.Fahrenheit(32)
print(f'temp0: {repr(temp0)}') # Celsius(0)
print(f'temp1: {repr(temp1.to_celsius())}') # Celsius(0.0)
print(temp0 != temp1) # False
print(temp0 > temp1) # False
print(temp0 < temp1) # False
print(temp0 >= temp1) # True
print(temp0 <= temp1) # True
print(temp0 == temp1) # True
Arithmetic operations ('+', '-', '*', '**', '/', '//', '%', ...):
from totemp import Newton, Rankine
if __name__ == '__main__':
temp0 = Newton(33)
temp1 = Rankine(671.67)
temp2 = temp0 + temp1
print('temp2:', temp2) # temp2: 65.99999999999999 ºN
print('temp2:', repr(temp2)) # temp2: Newton(65.99999999999999)
print('temp2:', temp2.value, temp2.symbol) # temp2: 65.99999999999999 ºN
print((temp0 + temp1).rounded()) # 66 ºN
print(repr((temp0 + temp1).rounded())) # Newton(66)
print(temp2 + 12.55) # 78.54999999999998 ºN
print((12 + temp2.rounded())) # 78 ºN
ToTemp classes can work with many built-in Python functions:
from math import floor, ceil, trunc
from totemp import Reaumur
if __name__ == '__main__':
temp = Reaumur(100.4)
float(temp) # 100.4
int(temp) # 100
round(temp) # Reaumur(100)
abs(temp) # Reaumur(100)
floor(temp) # Reaumur(100)
ceil(temp) # Reaumur(101)
trunc(temp) # Reaumur(100)
divmod(temp, temp0 := Reaumur(25.1)) # (Reaumur(4.0), Reaumur(0.0))
Temperature Instance Conversions:
import totemp
if __name__ == '__main__':
temp = totemp.Fahrenheit(32)
print(temp.to_celsius()) # 0.0 ºC
print(temp.to_fahrenheit()) # 32 ºF
print(temp.to_delisle()) # 150.0 ºDe
print(temp.to_kelvin()) # 273.15 K
print(temp.to_newton()) # 0.0 ºN
print(temp.to_rankine()) # 491.67 ºR
print(temp.to_reaumur()) # 0.0 ºRé
print(temp.to_romer()) # 7.5 ºRø
And that's it, that's my first ever project, some feedback or collaborations would be wonderfull! Proud of doing this, always gratefull for the friends and other programmer dudes of the internet that helped me make this project happen.