Installation and Usage

Installation

Install the package (or add it to your requirements.txt file):

$ pip install totemp

Or, in poetry environments:

$ poetry add totemp

Basic Usage and Representation

Note

The Base Class is the one who makes the magic happen in this package, for all the module detailed information go check every single class, its methods, properties and other implementations there.

Thanks to Daniil Fajnberg we could finally solve the problems when trying to implement this base class.

When working with temperature scales representations and conversions, we often see a lack of “straight to the point” solutions beside of searching online for every single mathematical formula.

In that sense, everytime I (Edson) tried to represent those temperatures (using packages or implementing my own code) to automate any calculation/operation/representation, I was often stuck with little to none precise and simple way of doing it.

This package aims to bring the simple and straight to the point, but precise, Object Oriented experience of working with temperature scale data types.

For example, this:

 1from totemp import Celsius, Fahrenheit, Kelvin, Delisle
 2
 3if __name__ == '__main__':
 4    temps: list = [Celsius(12), Celsius(25), Celsius(50)]
 5    print(temps[0])
 6    print(temps)
 7
 8    # Converts all Celsius objects using to_fahrenheit() method
 9    temps = list(map(Celsius.to_fahrenheit, temps))
10    print(temps[0])
11    print(temps)
12
13    # Unpacks the temperature objects list
14    temp0, temp1, temp2 = temps
15    print(temp0.__repr__())
16    print(temp0.__str__())
17    print(temp0.value)
18    print(temp0.symbol)
19
20    # Converts to Kelvin and then rounds the value
21    temp1 = temp1.to_kelvin()
22    print(temp1)
23    print(temp1.rounded())
24
25    # Converts to Delisle
26    temp2 = temp2.to_delisle().rounded()
27    print(type(temp2))
28    print(type(temp2.value), f'-> {temp2.value}')

Outputs:

From line 5 and 10:

>>> '12 ºC'
>>> '53.6 ºF'

Calls to the __str__ method of the temperature object returns
"`value`º `symbol`". That means that every call that need string
representations of the temperature (such as print(), str() or
`object`.__str__()) returns the value and its official symbol as
a string.

From line 6 and 11:

>>> [Celsius(12), Celsius(25), Celsius(50)]
>>> [Fahrenheit(53.6), Fahrenheit(77.0), Fahrenheit(122.0)]

And here we can see the "real representation" of the objects,
shown by **__repr__** special method, that specifies its nature
(which scale it is and it's value, and we can create a "new"
object with those representations).

From line 15, 16, 17 and 18:

>>> 'Fahrenheit(53.6)'  # __repr__()
>>> '53.6 ºF'  # __str__()
>>> 53.6  # `value` property
>>> 'ºF'  # Official `symbol` property

Both special methods and the symbol property returns
strings, but value is numeric, a float.

From line 22 and 23:

>>> 298.15000000000003 K
>>> 298 K

Here we can see the calculation precision and the
simplicity to make the result to be rounded, to
become an aproximate int value.

From line 27 and 28:

>>> <class 'totemp.temperature_types.Delisle'>
>>> <class 'int'> -> 75

And now we have two type outputs, the first one is
the type of the temp2 object and the other is the type
of its value.

Arithmetic and comparison operations

Now we can look at the really interesting part of working with ToTemp temperature objects: perform operations between temperature data types and its iterations with other numeric data types.

Let’s go straight to it:

 1import totemp as tp
 2
 3if __name__ == '__main__':
 4    temp0, temp1 = tp.Celsius(0), tp.Fahrenheit(32)
 5
 6    # Celsius(0) > Fahrenheit(32)
 7    if temp0 > temp1:
 8        print(f'`temp0`->{temp0} is greater than `temp1`->{temp1}')
 9    elif temp0 < temp1:
10        print(f'`temp0`->{temp0} is not greater than `temp1`->{temp1}')
11    else:
12        print('What...is...happening...?')

Outputs:

From line 12:

>>> What...is...happening...?

As you are probably thinking:

  • this doesn’t make senseor it does?

When doing comparisons between temperature data types what are we trying to achieve? To check if the objects “are the same” or to check if the values equivalent? Or one is greater/lesser than another?

For example, comparing int(1) == float(1) would return True, and that’s exactly what’s happening in our temperature comparision.

The __gt__ special method (and most of the other comparision and arithmetic special methods) checks if the object being compared to the calling class is an Temperature Type or a float/integer, if other is a Temperature, it attempts to convert the other object to the calling class and then return the result of the evaluation (to be printed, in our case).

Another example, with the same objects:

 1import totemp as tp
 2
 3if __name__ == '__main__':
 4    temp0, temp1 = tp.Celsius(0), tp.Fahrenheit(32)
 5
 6    print(f'temp0: {repr(temp0)}')
 7    print(f'temp1: {repr(temp1.to_celsius())}')
 8
 9    print(temp0 > temp1)
10
11    print(temp0 < temp1)
12
13    print(temp0 == temp1)
14
15    print(temp0 != temp1)
16
17    print(temp0 >= temp1)
18
19    print(temp0 <= temp1)

Note

Using repr() just for better visualization

Outputs:

From lines 6 and 7:

>>> temp0: Celsius(0)
>>> temp1: Celsius(0.0)

The comparision/arithmetic implementation attempts to convert the value
of other and then evaluate the expression.

That meaning:
`temp0` > `temp1.` is the same as `temp0` > `temp1.to_celsius()`

The values being compared here are the equivalent values already converted!
All that because of the calling class, `temp0` is an Celsius instance, so it
will trigger a conversion of `other` to be compared with after.

From lines 9 and 11:

>>> False
>>> False

The value of `temp0` isn't greater or lesser than `other` value, it is equal.

From lines 13 and 15:

>>> True
>>> False

After the conversion of `temp1` (Celsius(0.0)) we could see that `temp1`
has the same value, or better saying, has equivalent value to `temp0`.

From lines 17 and 19:

>>> True
>>> True

And, as we saw in the previous outputs (from lines 13 and 15), comparisions
that declare ">=" or "<=" would return True in that case, even though they
aren't greater or lesser than each other, they are indeed equivalents.

After understanding how comparisions are done, we can now see how the arithmetic operations work.

Look at this:

 1from totemp import Newton, Rankine
 2
 3if __name__ == '__main__':
 4    temp0 = Newton(33)
 5    temp1 = Rankine(671.67)
 6
 7    temp2 = temp0 + temp1
 8
 9    print('`temp2`:', temp2)
10    print('`temp2`:', repr(temp2))
11    print('`temp2`:', temp2.value, temp2.symbol)
12
13    print((temp0 + temp1).rounded())
14    print(repr((temp0 + temp1).rounded()))
15
16    print(temp2 + 12.55)
17    print((12 + temp2.rounded()))

Outputs:

From lines 9, 10 and 11:

>>> `temp2`: 65.99999999999999 ºN
>>> `temp2`: Newton(65.99999999999999)
>>> `temp2`: 65.99999999999999 ºN

Just as the comparisions, most of the arithmetic operations
that can be performed by the objects attempts to convert `other`
to the same type as the calling class (in this case, to Newton).

From line 13 and 14:

>>> 66 ºN
>>> Newton(66)

And, if needed, we can work with aproximate results too,
we could aproximate just the values of `temp0` or `temp1`,
none of them or even both before the operation actually
happen.

The thing is that every object can work as an aproximate or
precise value of itself to perform more "embracing" operations,
that the limits are mostly the way the developer/user is using
it.

From line 16 and 17:

>>> 78.54999999999998 ºN
>>> 78 ºN

Finally, as we can see, using ints and floats, even if the calling
object isn't the temperature scale, it can return the right result.

Note

ToTemp classes can work with many built-in Python functions:

And temperature objects accept this kind of syntax:

>>> temp = Celsius(12)
>>> temp0 = -temp
>>> temp1 = -temp0
>>>
>>> print(temp0)  # -12 ºC
>>> print(repr(temp0))  # Celsius(-12)
>>>
>>> print(temp1)  # 12 ºC
>>> print(repr(temp1))  # Celsius(12)

So, with that shown, we can already assume that the other arithmetic operations do the same (attempts to convert other to the same type as the calling class, if don’t, it attempts to apply other to value directly).

Temperature Instance Conversions

Every temperature class has 7 conversions “from self to another” class which contains the converted value and one that returns a new instance of itself with the same value.

All “to_*” methods calls the convert_to() abstract method that was inhehited from the Base Class, this implies that every class implements individual calculations for each of the following conversions:

  • to_celsius() -> returns a Celsius object;

  • to_fahrenheit() -> returns a Fahrenheit object;

  • to_delisle() -> returns a Delisle object;

  • to_kelvin() -> returns a Kelvin object;

  • to_newton() -> returns a Newton object;

  • to_rankine() -> returns a Rankine object;

  • to_reaumur() -> returns a Réaumur object;

  • to_romer() -> returns a Rømer object.

Just an example:

 1import totemp as tp
 2
 3if __name__ == '__main__':
 4    temp = tp.Fahrenheit(32)
 5
 6    print(temp.to_celsius())
 7    print(temp.to_fahrenheit())
 8    print(temp.to_delisle())
 9    print(temp.to_kelvin())
10    print(temp.to_newton())
11    print(temp.to_rankine())
12    print(temp.to_reaumur())
13    print(temp.to_romer())

Outputs:

From line 6 and 13:

>>> 0.0 ºC
>>> 32 ºF
>>> 150.0 ºDe
>>> 273.15 K
>>> 0.0 ºN
>>> 491.67 ºR
>>> 0.0 ºRé
>>> 7.5 ºRø

Now using convert_to():

 1from totemp import *
 2
 3if __name__ == '__main__':
 4    temp = tp.Fahrenheit(32)
 5
 6    print(temp.convert_to(Celsius))
 7    print(temp.convert_to(Fahrenheit))
 8    print(temp.convert_to(Delisle))
 9    print(temp.convert_to(Kelvin))
10    print(temp.convert_to(Newton))
11    print(temp.convert_to(Rankine))
12    print(temp.convert_to(Reaumur))
13    print(temp.convert_to(Romer))

Outputs:

From line 6 and 13:

>>> 0.0 ºC
>>> 32 ºF
>>> 150.0 ºDe
>>> 273.15 K
>>> 0.0 ºN
>>> 491.67 ºR
>>> 0.0 ºRé
>>> 7.5 ºRø

And that’s an overall vision of the package and what it can do.

Now, you can go to the Module section.