Some module design patterns

There are three commonly seen design patterns for Python modules:

  • Importable library modules: These are meant to be imported. They contain definitions of classes, functions, and perhaps some assignment statements to create a few global variables. They do not do any real work; they can be imported without any worry about the side effects of the import operation. There are two use cases that we'll look at:
    • Whole module: Some modules are designed to be imported as a whole, creating a module namespace that contains all of the items.
    • Item collection: Some modules are designed to allow individual items to be imported, instead of creating a module object; the math module is a prime example of this design.
  • Runnable script modules: These are meant to be executed from the command line. They contain more than class and function definitions. A script will include statements to do the real work. The presence of side effects means they cannot be meaningfully imported.
  • Conditional Script modules: These modules are hybrids of the two aforementioned use cases: they can be imported and they can also be run from the command line. These modules will have the main-import switch as described in the Python Standard Library, in the __main__ – Top-level script environment section.

Here's the conditional script switch from the library documentation:

if __name__ == "__main__": 
    main() 

This requires a main() function to do the work of the script. This design supports two use cases: both runnable and importable. When the module is run from the command line, it evaluates main() and does the expected work. When the module is imported, the function will not be evaluated, and the import will simply create the various definitions without doing any real work.

We suggest something a bit more sophisticated, as shown in Chapter 18, Coping with the Command Line:

if __name__ == "__main__": 
    with Setup_Logging(): 
        with Build_Config() as config:     
            main = Simulate_Command() 
            main.configure(config)
            main.run() 

This leads to the following essential design tip:

Importing a module should have few side effects.

Creating a few module-level variables is an acceptable side effect of an import. The real work – accessing network resources, printing output, updating files, and other kinds of processing – should not happen when a module is being imported.

A main script module without a __name__ == "__main__" section is often a bad idea because it can't be imported and reused. Beyond that, it's difficult for documentation tools to work with a main script module, and it's difficult to test. The documentation tools tend to import modules, causing work to be done unexpectedly. Similarly, testing requires care to avoid importing the module as part of a test setup.

In the next section, we'll compare a module with a class definition. The two concepts are similar in many respects.

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

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