With the introduction of functions, a new world of additional resources has been opened up. Python has a large number of libraries, referred to as modules, that can be imported and used by your programs. They are similar to the built-in functions, but large sets of libraries have specific purposes. This chapter will discuss a few of the available modules and will explain how to get them into your program and how to take advantage of them.

You've seen a number of what we've referred to as built-in functions already. These functions are a core part of the Python language. Using the *len*, *max*, or *print* functions is a trivial task; just type in the keyword along with the required parameters, and the correct values are returned. Even mathematical operators, such as the addition and multiplication symbols, are available right out of the box.

One of the mathematical functions that we've missed is the square root. This is a fairly common operation in math, but certainly not one that is as common as adding or multiplying. As it turns out, Python does offer a way to access the square root function. To get there, we'll need to import a module called *math*.

The *math* module is a collection of mathematical functions that are less common than the core functions, but still very likely to be required in your programs at some point. To access them, we use an *import* statement and specify the name of the module.

```
```**import math**

It's as easy as that. Now, we've got access to the full suite of functions inside the *math* module. When calling a function inside of a module, you need to specify the module itself where the function exists in addition to the function name. It's not quite like writing a function and just calling *getNumber*. Modules define a namespace, and methods like *sqrt* (the square root function that we're about to encounter) exist inside that namespace. The namespace is referenced like this:

```
>>>
```**import math**

>>> **math.sqrt(4)**

2.0

This function call looks almost exactly like the other function calls except for the fact that a module is specified with the function. With the module imported, methods in *math* can be used just like the other functions. Let's modify the division program from earlier so that the square root function is used instead.

```
```**import math**

**def getNumber(minimum_value=False, input_text="Enter a number: "):**

** done = False**

** while not done:**

** try:**

** user_number = int(input(input_text))**

** except ValueError:**

** print("You didn't enter a number! Shame on you.")**

** continue**

** if minimum_value == False or user_number >= minimum_value:**

** done = True**

** else:**

** print("Your number was too low!")**

** print("The minimum value is {0}.".format(minimum_value))**

** return user_number**

**user_number = getNumber(minimum_value=0, input_text="Enter a number to obtain the square root: ")**

**print("The square root of {0} is {1}.".format(user_number, math.sqrt(user_number)))**

Enter a number to obtain the square root: **25**

The square root of 25 is 5.0.

A call to *math.sqrt* requires a single numeric value as input, and a *float* number equal to the square root of the source parameter is returned. The *math* module has a whole set of useful functions, serving as an ideal example of a situation where you'd benefit from offloading a suite of related functions into a single location.

Included in the *math* functions are some helpful rounding operations. The *round* function is available as a built-in function, so we don't need to import *math* to use it. Two additional functions called *ceil* (short for ceiling) and *floor* can be used to round-up or round-down.

```
```**import math**

**x = 2.4**

**print("x = {0}".format(x))**

**print("Round: {0}".format(round(x)))**

**print("Ceiling: {0}".format(math.ceil(x)))**

**print("Floor: {0}".format(math.floor(x)))**

x = 2.4

Round: 2

Ceiling: 3

Floor: 2

Another useful module called *random* contains several random number generators, along with other related functions including ways to randomise sequences like lists.

Let's say, for example, that you wanted to build a dice-rolling program.

```
```**import random**

**for i in range(10):**

** print("Rolling a six sided die: {0}".format(random.randint(1, 6)))**

Rolling a six sided die: 3

Rolling a six sided die: 1

Rolling a six sided die: 4

Rolling a six sided die: 5

Rolling a six sided die: 3

Rolling a six sided die: 2

Rolling a six sided die: 1

Rolling a six sided die: 6

Rolling a six sided die: 4

Rolling a six sided die: 3

The *random.randint* function takes two numbers and returns a random number inside the inclusive range defined by the input. What that means is that in the example above, we're asking for an integer between 1 and 6.

You can also use the *random* module to shuffle lists of data in place, similar to sorting or reversing a list. Let's use *random* to build a list of randomly-generated integers, to sort and shuffle the list, and then to return an element at random.

```
```**import random**

**my_list = []**

**for i in range(10):**

** my_list.append(random.randint(1, 100))**

**print(my_list)**

**my_list.sort()**

**print(my_list)**

**random.shuffle(my_list)**

**print(my_list)**

**print(random.choice(my_list))**

[52, 39, 54, 62, 59, 95, 98, 10, 45, 28]

[10, 28, 39, 45, 52, 54, 59, 62, 95, 98]

[54, 10, 39, 98, 95, 59, 52, 62, 45, 28]

39

An an interesting aside, "random" numbers, are actually "pseudo-random". These numbers only appear random. A random number generator has a seed value that acts as a way of determining the starting random number, along with all successive random values after that. If you start with the same seed every time, you actually get the same set of random numbers. Generally, the computer will use its clock time as the starting seed, so unless you have the ability to travel back in time to the same exact moment that your code was executed the first time, you're extremely unlikely to have the same random values showing up. You can play with this quirk by setting the random seed manually and generating lists of random values.

```
```**import random**

**print("Random lists with a random seed:")**

**for x in range(5):**

** my_list = []**

** for i in range(10):**

** my_list.append(random.randint(1, 100))**

** print(my_list)**

**print("Random lists with a non-random seed:")**

**for x in range(5):**

** my_list = []**

** random.seed(0)**

** for i in range(10):**

** my_list.append(random.randint(1, 100))**

** print(my_list)**

Random lists with a random seed:

[87, 10, 92, 61, 42, 3, 21, 32, 85, 11]

[63, 96, 27, 95, 54, 97, 94, 77, 3, 85]

[93, 13, 7, 58, 69, 3, 30, 48, 40, 30]

[47, 79, 13, 74, 14, 95, 41, 8, 60, 66]

[90, 18, 41, 72, 8, 77, 24, 45, 6, 85]

Random lists with a non-random seed:

[85, 76, 43, 26, 52, 41, 79, 31, 48, 59]

[85, 76, 43, 26, 52, 41, 79, 31, 48, 59]

[85, 76, 43, 26, 52, 41, 79, 31, 48, 59]

[85, 76, 43, 26, 52, 41, 79, 31, 48, 59]

[85, 76, 43, 26, 52, 41, 79, 31, 48, 59]

Wacky, eh?

We'll talk more about additional modules as we move forward. For now, let's look at how you can write your own modules to save time by not copying and pasting the useful functions you develop. Naturally, modules can be defined by a programmer. If you've got a few input function helpers to get numbers or other values, you can set up your own importable module that will make it easier to write the code you want to write.

To begin, let's create a new file in IDLE. Save it with the name *getinput.py*, and don't forget the *.py* extension! This new file will become the module to import. You can think of the import statement as a way of asking Python to include an existing file right into your code at the point where you include the import statement. Any functions that you define in *getinput.py* will then become accessible to your source code that imports *getinput* in the future. Let's start with the previous *getNumber* function. Copy and paste *getNumber* into the new *getinput.py* source file, and save it.

Now go back to your other source code file, and at the very top of the file, write the following lines:

```
```**import getinput**

**getinput.getNumber()**

You don't need the *.py* extension in *import*; Python is able to infer that an *import* is asking for a file that ends in .py, so you can just omit the extension. Now try to run your file. If all goes well, you should see the following:

```
```**Enter a number: **

If you see that, congratulations! You've written a module! You no longer have to copy and paste *getNumber* in each of your future source files. You can just import the *getinput* module, a Python source file that you've written yourself and placed in the same directory as your source file, and you've got access to the right functions. Remember, you need the *getinput* prefix for the *getNumber* function, and the module has to be in the same directory as your current working file so that Python can find it.

Let's add some more code to *getinput*. How about a function that asks the user for a minimum and a maximum value, and returns a random number in that range using the *random* module. In *getinput.py*, add the following line to the top of the file.

```
```**import random**

And at the bottom of the file, add the following new function.

```
```**def getRandomRangeNumber():**

** min_number = getNumber(input_text="Enter the minimum value: ")**

** max_number = getNumber(minimum_value=min_number, input_text="Enter the maximum value: ")**

** return random.randint(min_number, max_number)**

This is a new function that asks the user for a minimum value and a maximum value that must be at least as big as the minimum they just entered. Using this range, it queries *random.randint* for a random number, and returns it.

Why doesn't the new *getRandomRangeNumber* need to prefix the calls to *getNumber* with the *getinput* namespace? It's because *getRandomRangeNumber* is in the same namespace; all the functions in the same namespace know about each other, so if you're calling one from the other, you don't need to specify that we're in the current file.

Now let's modify the original source that uses *getInput* so that it calls the new method we just wrote.

```
```**import getinput**

**print(getinput.getRandomRangeNumber())**

Enter the minimum value: **5**

Enter the maximum value: **25**

18

If you want to add in additional methods to your module, just place them in *getinput.py* and they'll be accessible to other Python source files that import the module. Awfully convenient, isn't it?

In some rare instances, you might want to only import a small section of the module. It might be the case that you feel you'll only need the *sqrt* function from *math*, or that you know that a module is itself broken down into further modules -- modules can actually be nested inside one-another, and we'll see an example of this.

One module that you might get a lot of use from is *datetime*. The *datetime* collection of functions and objects gives you a way to collect and manipulate dates and times. It gives you a set of methods for formatting date strings for printing to the screen. It also allows you to perform arithmetic on dates, as you might do when asking for the date two days from now. The *now* function gives you the current date and time.

```
```**import datetime**

**print(datetime.datetime.now())**

2010-11-24 16:17:58.521000

It looks a little unusual when *datetime* is entered twice like that. This is an artifact of the *datetime* module and how it's been structured in the original source. It turns out that there's a *datetime* object inside the *datetime* module that is responsible for getting values like the current date. The reason for this is that the *datetime* module does something analogous to defining *datetime* as a type. It sets up an object called *datetime*, and that new *datetime* object is assigned a value corresponding to the current time.

What? This might be a little confusing, and that's completely fair. The next chapter will talk about classes, and will open up some more of these details. For the moment, consider that importing the *datetime* module defines the *datetime* namespace (the first *datetime*), and inside that namespace is a *datetime* object (the second *datetime*) upon which the *now* function is called. Confused yet?

For this very reason, we can use a slightly different form of the *import* statement. We can ask Python to import the *datetime* object (not the module) directly into the current namespace, instead of into the *datetime* namespace.

```
```**from datetime import datetime**

**print(datetime.now())**

2010-11-24 16:17:58.521000

As a simplification of this, we can also write the following code:

```
```**from math import sqrt**

**sqrt(4)**

2.0

The *from* statement used in conjunction with *import* asks Python to import a subset of the module instead of the whole thing. In the case of the *math* module, we ask Python to just bring in the *sqrt* function, but not the whole module.

Modules offer a way of organizing large sets of code in the same way that functions allow you to place related lines of code in a single location. They save you the hassle of copying and pasting the functions you use most frequently by tucking them away behind an *import* statement, ready to be used at your demand. If you don't need to use them, they're still safely organized in your source code directory.

These abstractions move programming from a hacky collection of random lines of code towards a well-structured piece of engineering. In fact, when we refer to software engineering as a practice, it is with the intent of approaching the rigour that professional engineers in the physical sciences apply to their work. When building a bridge, the designers go to great lengths to ensure that everything makes sense, to prevent the bridge from collapsing. It might seem strange to think about software in the same way at first, but when you consider that software is now found in cars, airplanes, and hospital equipment, the demand on software developers to write well-structured and maintainable code is greater than ever. Functions and modules are a step in this direction, and a step away from the chaotic development practices that many developers fall in to.

With practice, you'll figure out the best ways to structure your code, giving you an ideal amount of reuse and a minimal amount of rewriting. It's always really nice to find yourself in need of a function, and then realizing that you're already written it and can import it with a single line of code.

1) Go back and look at some of the code that you're written previously when working through this book. Find some examples that you might like to use in other programs that you write, and extract them out to a module. Save the file in your working directory, and write some test programs to import the module and use the new functions.