Recipe6.1.Converting Among Temperature Scales


Recipe 6.1. Converting Among Temperature Scales

Credit: Artur de Sousa Rocha, Adde Nilsson

Problem

You want to convert easily among Kelvin, Celsius, Fahrenheit, and Rankine scales of temperature.

Solution

Rather than having a dozen functions to do all possible conversions, we can more elegantly package this functionality into a class:

class Temperature(object):     coefficients = {'c': (1.0, 0.0, -273.15), 'f': (1.8, -273.15, 32.0),                     'r': (1.8, 0.0, 0.0)}     def _ _init_ _(self, **kwargs):         # default to absolute (Kelvin) 0, but allow one named argument,         # with name being k, c, f or r, to use any of the scales         try:             name, value = kwargs.popitem( )         except KeyError:             # no arguments, so default to k=0             name, value = 'k', 0         # error if there are more arguments, or the arg's name is unknown         if kwargs or name not in 'kcfr':             kwargs[name] = value             # put it back for diagnosis             raise TypeError, 'invalid arguments %r' % kwargs         setattr(self, name, float(value))     def _ _getattr_ _(self, name):         # maps getting of c, f, r, to computation from k         try:             eq = self.coefficients[name]         except KeyError:             # unknown name, give error message             raise AttributeError, name         return (self.k + eq[1]) * eq[0] + eq[2]     def _ _setattr_ _(self, name, value):         # maps settings of k, c, f, r, to setting of k; forbids others         if name in self.coefficients:             # name is c, f or r -- compute and set k             eq = self.coefficients[name]             self.k = (value - eq[2]) / eq[0] - eq[1]         elif name == 'k':             # name is k, just set it             object._ _setattr_ _(self, name, value)         else:             # unknown name, give error message             raise AttributeError, name     def _ _str_ _(self):         # readable, concise representation as string         return "%s K" % self.k     def _ _repr_ _(self):         # detailed, precise representation as string         return "Temperature(k=%r)" % self.k

Discussion

Converting between several different scales or units of measure is a task that's subject to a "combinatorial explosion": if we tackle it in the apparently obvious way, by providing a function for each conversion, then, to deal with n different units, we will have to write n * (n-1) functions.

A Python class can intercept attribute setting and getting, and perform computation on the fly in response. This power enables a much handier and more elegant architecture, as shown in this recipe for the specific case of temperatures.

Inside the class, we always hold the measurement in one reference unit or scale, Kelvin (absolute) degrees in the case of this recipe. We allow the setting of the value to happen through any of four attribute names ('k', 'r', 'c', 'f', abbreviations of the scales' names), and compute and set the Kelvin-scale value appropriately. Vice versa, we also allow the "getting" of the value in any scale, through the same attribute names, computing the result on the fly. (Assuming you have saved the code in this recipe as te.py somewhere on your Python sys.path, you can import it as a module.) For example:

>>> from te import Temperature >>> t = Temperature(f=70)        # 70 F is... >>> print t.c                    # ...a bit over 21 C 21.1111111111 >>> t.c = 23                     # 23 C is... >>> print t.f                    # ...a bit over 73 F 73.4

_ _getattr_ _ and _ _setattr_ _ work better than named properties would in this case, since the form of the computation is the same for every attribute (except the reference 'k' one), and we only need to use different coefficients that we can most handily keep in a per-class dictionary, the one we name self.coefficients. It's important to remember that _ _setattr_ _ is called on every setting of any attribute, so it must delegate to object the setting of attributes, which need to be recorded in the instance (the _ _setattr_ _ implementation in this recipe does just such a delegation for attribute k) and must raise an AttributeError exception for attributes that can't be set. _ _getattr_ _, on the other hand, is called only upon the "getting" of an attribute that can't be found by other, "normal" means (e.g., in the case of this recipe's class, _ _getattr_ _ is not called for accesses to attribute k, which is recorded in the instance and thus gets found by normal means). _ _getattr_ _ must also raise an AttributeError exception for attributes that can't be accessed.

See Also

Library Reference and Python in a Nutshell documentation on attributes and on special methods _ _getattr_ _ and _ _setattr_ _.



Python Cookbook
Python Cookbook
ISBN: 0596007973
EAN: 2147483647
Year: 2004
Pages: 420

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net