 Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.

# Quantities and Units in Python

When you need to perform calculations with values that have an associated unit of measure, it is very common to make mistakes by mixing different units of measure. It is also common to perform incorrect conversions between the different units that generate wrong results. The latest Python release doesn't allow developers to associate a specific numerical value with a unit of measure. In this article, I look at three Python packages that provide different solutions to this problem and allow you to work with units of measure and perform conversions.

### Three Different Packages to Add Units of Measure

The need to associate quantities with units of measure in any programming language is easy to understand, even in the most basic math and physics problems. One of the simplest calculations is to sum two values that have an associated base unit. For example, say that you have two electrical resistance values. One of the values is measured in ohms and the other in kilo-ohms. To sum the values, you must choose the desired unit and convert one of the values to the chosen unit. If you want the result to be expressed in ohms, you must convert the value in kilo-ohms to ohms, sum the two values expressed in ohms, and provide the result in ohms.

The following Python code uses variables with a suffix that defines the specific unit being used in each case. You have probably used or seen similar conventions. The suffixes make the code less error-prone because you easily understand that `r1_in_ohms` holds a value in ohms, and `r2_in_kohms` holds a value in kilo-ohms. Thus, there is a line that assigns the result of converting the `r2_in_kohms` value to ohms to the new `r2_in_ohms` variable. The last line calculates the sum and holds the result in ohms because both variables hold values in the same unit of measure.

```r1_in_ohms = 500
r2_in_kohms = 5.2
r2_in_ohms = r2_in_kohms * 1e3
r1_plus_r2_in_ohms = r1_in_ohms + r2_in_ohms
```

Obviously, the code is still error-prone because there won't be any exception or syntax error if a developer adds the following line to sum ohms and kilo-ohms without performing the necessary conversions:

`r3_in_ohms = r1_in_ohms + r2_in_kohms`

There is no rule that assures that all the variables included in the `sum` operation must use the same suffix; that is, the same unit. There aren't invalid operations between variables that hold values with incompatible units. For example, you might sum a `voltage` value to a `resistance` value and the code won't produce any error warning.

Now, imagine that Python adds support for units of measure. Each numeric value can have an associated unit of measure enclosed within <>. The following three lines would replace the previous code with an easier to understand syntax:

```r1 = 500 <ohms>
r2 = 5.2 <kilo-ohms>
r1_plus_r2 = (r1 + r2) <ohms>```

`r1` holds a value of `500` and an associated unit of measure, `ohms`. `r2` holds a value of `5.2` and an associated unit of measure, `kilo-ohms`. Because each variable includes information about its unit of measure, the `sum` operation is smart and it can convert compatible units such as `ohms` and `kilo-ohms`. The last line sums the two values taking into account their unit of measure, and converts the result to the specified unit, `ohms`. The `r1_plus_r2` variable holds the result of the `sum` operation expressed in `ohms`. The following line would produce an exception because the units of measure are incompatible:

`sum = (10 <volts> + 500 <ohms>) <inches>`

However, the support should be smart enough to allow you to mix different length units. For example, the following line would produce a valid result in inches.

`sum = (10 <inches> + 1200 <centimeters>) <inches>`

Python doesn't support units of measure, but the three Python packages I examine here provide different ways to enable them. Each package takes a different approach. While none works as well as a native language feature would, these solutions do provide a baseline that you can improve according to your specific needs.

 Package Name Latest Version Python Package Web Page Numericalunits 1.12 http://pypi.python.org/pypi/numericalunits Pint 0.2.1 http://pypi.python.org/pypi/Pint Units 0.06 http://pypi.python.org/pypi/units

### Numericalunits: A Bare-Bones Solution

Numericalunits is a single Python module (numericalunits.py) that provides easy unit conversion features for your operations. You just need to follow two simple rules:

• To assign a unit of measure to a value, multiply the value by the unit.
• To express a value generated by its multiplication by the unit in a different unit, divide the value by that unit.

You simply need to add the following lines to import the module with an alias (`nu`) and execute the \$`reset_units` method to start working with the different units.

```import numericalunits as nu
nu.reset_units('SI')```

When you call `nu.reset_units('SI')`, Numericalunits uses standard SI units (short for Système Internationale d'Unités in French) for storing the values (see Figure 1). This way, any length value is stored in meters, no matter the length unit you specify in the multiplication. Read here if you want more information about SI base units. Figure 1: The SI base units and their interdependencies.

If you call the default `nu.reset_units()`, Numericalunits uses a random set of units instead of the standard SI units. I really don't like using a random set of units because it usually generates a loss of precision and results that lack accuracy. The only advantage of using random units is that you can check dimensional errors by running calculations twice and comparing whether the results match. You have to call `nu.reset_units()` before each calculation and compare the two values. I don't like this way of checking dimension errors because it adds a huge overhead and it is indeed error-prone. Thus, I suggest using Numericalunits as a unit conversion helper with the standard SI units initialization.

Numericalunits doesn't save information about the unit of measure in the numerical variable; therefore, there is no way to know which unit you used when you assigned the value. If you need more than a unit conversions helper, I suggest working with one of the other packages.

You can read the following line of code as "assign 500 ohms to `r1`."

`r1 = 500 * nu.ohm`

You can read the following line of code as "display the value of `r1` expressed in kilo-ohms."

`print(r1 / nu.kohm)`

The following line displays the value of `r1` expressed in ohms.

`print(r1 / nu.ohm)`

The following code uses Numericalunits to sum the values of `r1` and `r2`. Notice that the code is self-documented because you can easily see that `r1` holds a value in `ohms` and `r2` in `kilo-ohms`. The `r1_plus_r2` variable holds the result of the `sum` operation expressed in `ohms` and `r1_plus_r2_kohms` holds the result converted to `kilo-ohms`. Notice that you can sum the values of `r1` and `r2` without having to convert the units to ohms and the result will be accurate because of the way in which Numericalunits saves the values in the base units.

```import numericalunits as nu
nu.reset_units('SI')
r1 = 500 * nu.ohm
r2 = 5.2 * nu.kohm
r1_plus_r2 = r1 + r2
r1_plus_r2_kohms = r1_plus_r2 / nu.kohm
```

The following code uses Numericalunits to sum four `distance` values expressed in four different units of measure: meters, miles, centimeters, and feet. Numericalunits doesn't support plurals for the units. The `total_distance` variable holds the total distance expressed in feet.

```import numericalunits as nu
nu.reset_units('SI')
distance1 = 2500 * nu.m
distance2 = 2 * nu.mile
distance3 = 3000 * nu.cm
distance4 = 3500 * nu.foot
total_distance = (distance1 + distance2 + distance3 + distance4) / nu.foot
```

### Pint: A Complete Package

Pint is a Python package that allows you to work with numerical values associated to units of measure. Because Pint saves the magnitude and the associated units for any numerical type, you are able to know which unit you used when you assigned the magnitude value. You can perform arithmetic operations between compatible units and convert from and to different units. When you try to perform arithmetic operations on magnitudes that have incompatible units of measure, Pint raises a `Pint.unit.DimensionalityError` exception indicating that it cannot convert from one unit to the other before performing the arithmetic operation.

The `UnitRegistry` class stores the definitions and relationships between units. By default, Pint uses the `default_en.txt` unit definitions file. This file contains the different units and the prefixes that the `UnitRegistry` will recognize in plain text. You can easily edit this text file to add any unit you might need to support.

### More Insights To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.

## Featured Reports ## Featured Whitepapers 