Designing a reusable package

Let's now take what we've learned and apply it to the design and implementation of a useful Python package. In the previous chapter, we looked at the concept of encapsulating a recipe using a Python module. Part of each recipe is the notion of an ingredient, which has three parts:

  • The name of the ingredient
  • How much of the ingredient is needed
  • The units in which the ingredient is measured

If we want to work with ingredients, we need to be able to handle units properly. For example, adding 1.5 kilograms to 750 grams involves more than adding the numbers 1.5 and 750—you have to know how to convert these values from one unit to another.

In the case of recipes, there are a number of rather unusual conversions that you need to support. For example, did you know that three teaspoons of sugar equals one tablespoon of sugar? To handle these types of conversions, let's write a unit conversion library.

Our unit converter will have to be aware of all the standard units used in cooking. These include cups, tablespoons, teaspoons, grams, ounces, pounds, and so on. Our unit converter will need some way of representing a quantity, such as 1.5 kilograms, and of converting quantities from one unit to another.

As well as representing and converting quantities, we would like our library to be able to display quantities, automatically using the singular or plural version of the unit name as appropriate, for example, 6 cups, 1 gallon, 150 grams, and so on.

Since we're displaying quantities, it would also be helpful if our library could parse quantities. This way, the user could enter a value such as 3 tbsp and our library would know that the user entered a quantity of three tablespoons.

The more we think about this library, the more it seems like a useful tool in its own right. We thought of this in connection with our recipe-handling program, but it seems that this could be an ideal candidate for a reusable module or package.

Following the guidelines we looked at earlier, let's consider how we can generalize our library as much as possible to make it more useful in other programs and to other programmers.

Rather than just thinking about the sorts of quantities you might find in a recipe, let's change the scope of our library to handle any type of quantity. It could handle weights, lengths, areas, volumes, and possibly even units of time, force, speed, and the like.

Thinking of it like this, our library isn't so much a unit converter as a library that works with quantities. A quantity is a number and its associated units, for example, 150 millimeters, 1.5 ounces, or 5 acres. Our library, which we will call Quantities, will be a tool for parsing, displaying, and creating quantities, as well as converting quantities from one unit to another. As you can see, our initial concept for the library is now just one of the things that the library will be able to do.

Let's now design our Quantities library in more detail. We'd like the user of our library to be able to create a new quantity very easily. For example:

q = quantities.new(5, "kilograms")

We also want to be able to parse a string into a quantity value, like this:

q = quantities.parse("3 tbsp")

We then want to be able to display a quantity in the following manner:

print(q)

We also want to be able to tell what kind of value a quantity is representing, for example:

>>> print(quantities.kind(q))
weight

This will let us tell whether a quantity represents a weight, a length, or a distance, among others.

We can also retrieve the value and units for a quantity:

>>> print(quantities.value(q))
3
>>> print(quantities.units(q))
tablespoon

We also need the ability to convert a quantity into a different unit. For example:

>>> q = quantities.new(2.5, "cups")
>>> print(quantities.convert(q, "liter"))
0.59147059125 liters

Finally, we would like to be able to get a list of all the kinds of units that our library supports and the individual units of each kind:

>>> for kind in quantities.supported_kinds():
>>>     for unit in quantities.supported_units(kind):
>>>         print(kind, unit)
weight gram
weight kilogram
weight ounce
weight pound
length millimeter
...

There is one final feature that our Quantities library will need to support: the ability to localize units and quantities. Unfortunately, the conversion values for certain quantities will vary depending on whether you are in the United States or elsewhere. For example, in the U.S. a teaspoon has a volume of approximately 4.93 cubic centimeters, while in the rest of the world a teaspoon is considered to have a volume of 5 cubic centimeters. There are also naming conventions to deal with: in the U.S. the base unit of length in the metric system is referred to as a meter, while in the rest of the world the same unit is spelled metre. Our unit will have to handle both the different conversion values and the different naming conventions.

To do this, we will need to support the notion of a locale. When our library is initialized, the caller will specify the locale under which our module should operate:

quantities.init("international")

This will affect the conversion values and spelling used by the library:

Given the complexity of our Quantities library, it doesn't make sense to try and squeeze all this into a single module. Instead, we'll break our library up into three separate modules: a units module which defines all the different type of units that we support, an interface module which implements the various public functions for our package, and a quantity module which encapsulates the concept of a quantity being a value and its associated unit.

These three modules will be combined into a single Python package, which we will call quantities.

Note

Note that we deliberately used the term library to refer to the system as we were designing it; this ensured that we didn't pre-empt our design by thinking of it as a single module or as a package. Only now is it clear that we are going to write a Python package. Often, something that you think of as a module will end up growing into a package. Occasionally the opposite happens. It's important to be flexible about this.

Now that we have a good design for our Quantities library, what it will do, and how we'd like to structure it, let's start writing some code.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.148.144.228