Chapter 7. Organizing Programs

In Chapter 6, you began using Python's features to create separate classes that can be used to create entirely self-contained objects. Classes and the objects that are created from them are tools that enable you to gather data and functions into a contained space so Figure that they can be viewed as part of a larger entity.

So far, the definitions of classes have all been in a single file and were not run in the way you normally think of programs being run. Instead, they were invoked interactively so that you could use them as you would from within another program. However, if you wanted to use the classes you've written with what you know so far, you would make the same file that defined the classes the program. That means putting all of the classes at the beginning of the file, and the important decision-making code at the end. The end is where it takes the most time to find the code that you're going to want to find the most often.

Another cautionary note needs to be sounded. Classes are very useful, but not all problems should be solved by creating a class. Sometimes the work of designing them is overkill, and other times what you really need are functions that don't require the long life span that data and methods can have in objects.

To make Python more useful, therefore, it offers you the great feature of enabling you to create modules that create a named scope for functions and data, but which are simpler than classes and objects. Modules give you a tool to separate your program into distinctly named pieces, without using classes to do it. In fact, classes can be defined within a module.

As an extension of this, you can also divide these modules into different files; Python calls this feature a package. Packages enable you to divide your programs among several files and even into separate directories to help you organize your programs.

So far, you have only been introduced to intrinsic pieces of the Python language — things that deal with how Python itself works. Python is also very flexible, and though it comes with a small core set of features, these are expanded in a variety of modules. To extend Python to use features provided by the operating system, there is a module called os. To extend Python to have networking features, Python provides modules that offer both low-level networking (such as sockets) and higher-level protocols (such as http, ftp, and so on). Many modules come with Python, but because it is very easy to write modules, a variety of additional modules are available from third parties, both commercial and free.

In this chapter you learn:

  • How to write simple modules for your own use or to share.

  • Some of the bundled Python modules.

  • The concept of importing modules.

  • How to use packages to contain useful functions and names, separately from the global scope.

  • You also find out more about how scope can be used to your advantage for tasks such as testing your packages.

Modules

Modules present a whole group of functions, methods, or data that should relate to a common theme. Such a theme might be networking components (see Chapter 16), performing more complicated work with strings and text (see Chapter 12), dealing with graphical user interfaces (see Chapter 13), and other services.

After you've learned how to program in a language, you often find that you need to work with components that the language doesn't initially bundle. Python, by itself, is no different. At its core, it is a very small and simple language that doesn't offer many special features. However, because of its simplicity, it is easy to use as a platform that can be extended with additional functions and objects that can be used by anyone.

Importing a Module So That You Can Use It

To make a module usable, two things need to be available. First, the module itself has to be installed on the system. For the most part, you'll find that a lot of the basic things you want to do, such as reading and writing files (more on this in Chapter 8) and other fundamental important things that differ between platforms, are available as bundled modules with Python — that is, they are free and universally available with the language.

The simplest way to begin using a module is with the import keyword:

import sys

This will import the module named sys that contains services Python offers that mostly involve system-specific items. This means that it relates to things that involve how the system works, how a particular installation of Python is installed, or how the program you've written was invoked from the command line.

To start looking at modules, you're also going to begin to write in a style that facilitates running the file you're working on by itself, as a standalone program. To that end, create a file called ch7.py and type the following:

#!/usr/bin/env python3.1
# Chapter 7 module demonstration
import sys

The first line is for users of Linux and other UNIX systems (or Python under a UNIX-based environment like Cygwin). This is a way to get the python3.1 binary to run in case other Python interpreters are on the system. See the website for this book for more information on running Python. For Windows and Macintosh systems, the file extension should provide information that the operating system needs to launch the Python interpreter, whether it's python, python3.1, or some other name when it's installed on your system (although some configuration may be needed). See the website for more information on this, too.

Making a Module from Pre-existing Code

To create a module, all you need to do is choose a name for your module and open a file with that name and the extension .py in your editor. For example, to create a Foods module, you only have to create a file called Foods.py. When that's finished, you can import it using the name "Foods" without the .py at the end. That's it! You've imported a simple module.

Using Modules — Starting with the Command Line

So far, you've started by using import with a module name by itself. When a module is imported this way, all of the names it contains are put into a scope that is named for the module — that is, the name that was used in the import statement.

For example, in the case of sys, everything available is referred to by using the name sys, followed by a period, and then the name inside of sys, such as sys.path or sys.copyright, which, as it suggests, specifies the copyright on Python (programmers love to be clever like that). Now that you know how modules are structured, you can interactively explore the sys module with the Code Editor Python shell, or with the dir function, as you saw in Chapter 6. (dir will show you even more than the helpful dialog box in the Code Editor shell, because it shows private names that aren't part of the interface of the module. These concepts, which you've seen in classes and objects, still apply to modules!) You can also explore the docstrings that are present in the module and in the functions and classes provided by it.

On UNIX and UNIX-like environments, it's common to ask users to provide command-line parameters that will determine how a program as a whole will behave. This is conceptually very similar to how functions use parameters in Python. These command-line parameters show up in Python programs as a special name inside the sys module. That name is argv. This name may not make much sense at first, but it's an important term to know because it is common across most languages and platforms.

argv is an abbreviation for the term argument vector. In computer programming lingo, argument is another word for what you've seen called a parameter. This term is used with functions and when you run a program with parameters on the command line (another word for parameters and arguments on the command line is flags). A vector is another word for a list of options. In some languages, it has a very specific and different meaning, but Python doesn't make the same distinction, so you don't have to worry about it.

If you translate argv back through those definitions, you'll see that it simply means the parameters that were on the command line, accessible as a list! It's hard to convert that information into a short and comprehensible word that makes sense in English (or any other nonprogramming language that the author has heard of), so the term argv persists.

To print out the parameters from the command line, you just have to use sys.argv as you would with any other list:

>>>import sys
>>>print("This was given the command line parameters: %s" % sys.argv)

To make running this the same procedure on any platform, you can launch this from Code Editor. Select File

Using Modules — Starting with the Command Line

For testing programs that are changing and that aren't meant to be used interactively, you are generally better off using python -i or Run with Interpreter; this way, you can try running your program repeatedly, starting the program from the beginning each time. Figure 7-2 shows a pop-up showing optional command-line options.

Figure 7-2

Figure 7.2. Figure 7-2

Changing How Import Works — Bringing in More

Import can be used alone; when it's used that way, it creates a named scope from which everything in the module can be referenced. Sometimes it can be useful to have specific parts of the module brought into your program's top-level global scope, though. Eliminating the need to type the name of the module before the function or class you have to access reduces a lot of typing and makes your code a lot more straightforward. With your Foods module, you have to do the following to get an onion Omelet:

import Foods
r = Foods.Recipe()
onion_ingredients = Foods.Omelet(r, "onion")

You can see by this example that when you want to invoke or access something inside of a module, it means spelling out the entire path. You can quickly tire of doing this. However, you can change this behavior by bringing the names you want closer into your code by using the from modifier to the import command:

from Foods import Omelet
from Foods import Recipe
r = Recipe()
onion_ingredients = Omelet(r, "onion")

If you have to descend more levels, such as to (the made up food) Foods.Recipes.Breads.Muffins.Bran and you want to bring the names from Bran into the current scope, you'd write something similar. It would look like you'd expect:

from Foods.Recipes.Breads.Muffins import Bran

Packages

After you have a module built and in its own file, it's not uncommon to find that a single file runs headlong into organizational issues. Mainly, the issue is that an individual class becomes more useful on its own and may gain far more code than all of the rest of the classes in the module. This would be a good reason to move it to its own file, but that would break code that already uses the module! However, there is a solution.

To provide a structure for doing this, Python provides the organizational idea of packages. Packages use the structure of the directories (another name for folders) that every operating system uses to give you a methodology for making many files in the same directory look like a single module when they're used together.

You can start by simply making the directory. Let's break up the Foods module. First, you need to use a new name — Foods.py already exists, and it would be confusing to keep working with the module by calling it "Foods." Therefore, to work around that, start working on a new package, and call this new one the Kitchen package (this name is also general enough to leave you a lot of room for your imagination to work with later if you'd like to).

Simply enough, create a Kitchen directory. Then create a file in Kitchen called __init__.py (this name has to be the same name as the method in a class that you've seen already, and note that it has two underscores before and after the name). This file is the hint that tells Python that this is a package directory, and not just a directory with Python files in it. This is important because it ensures that you know you're responsible for maintaining this and controlling its behavior. This file has a lot of control over how the package is going to be used, because unlike a module, when a package is imported, every file in the directory isn't immediately imported and evaluated. Instead, the __init__.py file is evaluated, and here you can specify which files are used and how they're used!

Modules and Packages

Now that modules and packages have been defined, you will continue to see how to use them — mostly interchangeably. You'll generally have your attention drawn to where packages behave differently from a single module. Because the module has been named Foods and the package has been named Kitchen, you won't be confused when you're shown something that deals with a package instead of a module. Just remember: Kitchen references are highlighting packages; Foods references are highlighting modules.

Bringing Everything into the Current Scope

Note a special feature of modules: Sometimes you may want to have the entire contents of a module available without having to explicitly specify each name that is available from it. To do this, Python provides a special character, the asterisk, which can be used with the from ... import ... statement. It's important to understand that you can only import using the * when you are importing into the global scope:

from Foods import *

This would bring Omelet into your current scope, as well as everything else at the top of the recipe module. In other words, now you no longer have type Foods.Omelet(), just Omelet(), and you need to do this only once, instead of one time for each name you want to make local.

Packages can be made to work in a similar fashion, but underneath, they actually work differently. For packages, you need to specify the names you want to be provided when from ... import *, and these need to be stated explicitly. You can make the three modules in the Kitchen package available by using the __all__ list in __init__.py. Any names that appear in the __all__ list will be exported by the * but only those names.

The elements that are present in the __all__ list are the names of functions, classes, or data that will be automatically imported into the global scope of the program that is asked to import *.

You can expect users of modules and packages you write to automatically use the from ... import * syntax within their programs. To work with packages, you must specify a list of names that will be exported! However, if you have a large module, you can also create an __all__ list at the top of your module file, and it will also have the effect of restricting the names in the module in the same way as it would in a package.

Re-importing Modules and Packages

Programming involves a lot of trial and error. You will often realize that you've made a mistake in the work you've done in your module while you're in the shell interactively. Because you may have done a lot of typing to get your shell set up perfectly for your test before your problem module was loaded, you'd like to be able to fix your module and have Python re-load it so that you can save yourself the work of having to set up your session again. So far, you haven't been shown how to do this, but you can.

The first thing you need to know to do this is that it's normal for a common module to be required by multiple other modules and effectively be called up multiple times in the same program. When this happens, instead of going through the extra time it would take to re-load, re-evaluate, and re-compile the module each time (see the sidebar Compiling and .pyc Files), Python stashes away the name of the module, and where it came from, in a special dictionary of all the modules that have been imported so far, called sys.modules. In fact, when you use the Python shell from within Code Editor, it's already loaded sys and many other modules for you, so any time you've called it in your own, you've had this happen!

Basics of Testing Your Modules and Packages

There is a very interesting side effect of the scope that is created for modules. Within your program is always a special name, __name__, that tells you what the scope you're running in is called. For instance, if the value of __name__ were checked from within the Foods module, it would return the string 'Foods'.

One special reserved name, the name of the top-level global scope, is __main__. If you have a module that's normally never used directly, you can stick some code at the end that has one purpose in life — verifying that your module works! This is a great opportunity to make your testing easy.

You'll have many occasions when you see a module with the following code at the end:

if __name__ == '__main__':

You can use this statement at the end of your modules; and from this point on, you can have tests that will ensure that classes are made, that functions will return the values that you expect, or any other tests you can think of. It's very common as you program to have situations in which something that once worked suddenly breaks. It's always a great idea to place tests for these situations in your packages so that you never forget that they can happen, and you can be ahead of the game! There is a lot more information about testing in Chapter 12.

Summary

In the previous chapters, you learned how to write code at the interactive Python shell, as well as put code into individual files that can be run. In this chapter, you've been shown ways of organizing your programs into modules and packages.

Modules are distinct names that Python uses to keep a scope for local names. Within a module, a name can be used directly; however, from outside of a particular module (for instance, in the global top-level scope whose name is actually __main__), the names within a module can be accessed by first specifying the name of the module where the name you want to use is defined, followed by a period, followed by the name you're looking for. An example of this is sys.path. This enables you to use the same name in different modules for different purposes, without being confusing.

To use a module, it must be brought into your program with the import statement. Import will find a file with the name of the module you want to use, with the extension .py, and make it available. It does this by examining each of the directories in the list sys.path until it finds the file.

You will often want specific parts of a module to be available with less typing than the entire specification would require — the long form would be the name of the module, any intermediate modules (separated with periods), and then the name you actually want. In such cases, you can use the construct from ... import ... to just import names that you will be frequently using. When a module is imported, it is evaluated, and any code that is not inside of a function or a class will be evaluated.

When you have a lot of code to write, you can use a package to group your code into a structure that is provided by the underlying file system of your operation system. This structure begins with a directory (the same thing as a folder), which will be the name of the package when it is imported into your program. What makes a directory into a package is the presence of a file called __init__.py. This file will be read and parsed, and it can contain any code that could be useful to the entire package, such as data that should be available to all parts of the package, like version information, locations of important files, and so on, as well as import statements that could be required to bring in modules that will be needed in order for other parts of the package to work correctly.

When you have a package, the files in that package will not be automatically exported when a programmer requests it by using from ... import *, even if those files are modules that have been imported inside of __init__.py. With a package, the names that will be exported by default to this request have to be specified in a list called __all__.

The key things to take away from this chapter are:

  • You can import modules using the import keyword.

  • Argv is short for argument vector and refers to parameters that are on the command line. These parameters are accessible as a list by using sys.argv.

  • Packages use the structure of the directories (another name for folders) that every operating system uses to give you a methodology for making many files in the same directory look like a single module when they're used together.

  • To make the entire contents of a module available, use from modulename import *.

Exercises

Moving code to modules and packages is straightforward and doesn't necessarily require any changes to the code to work, which is part of the ease of using Python.

In these exercises, the focus is on testing your modules, because testing is essentially writing small programs for an automated task.

  1. Write a test for the Foods.Recipe module that creates a recipe object with a list of foods, and then verifies that the keys and values provided are all present and match up. Write the test so that it is run only when Recipe.py is called directly, and not when it is imported.

  2. Write a test for the Foods.Fridge module that will add items to the Fridge, and exercise all of its interfaces except get_ingredients, which requires an Omelet object.

  3. Experiment with these tests. Run them directly from the command line. If you've typed them correctly, no errors should come up. Try introducing errors to elicit error messages from your tests.

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

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