Chapter 18: Packages
This introduction explains why packages help organize related modules and distinguishes that meaning of package from a distributable file.
After this chapter you will be able to
- Packages for related modules. Explain why a growing project may benefit from combining related modules into a package rather than keeping one giant module or many unconnected modules.
- Package and module structure. Distinguish a module from a package by describing what each contains and how each gets its name.
- Package hierarchy and names. Explain how mathproj divides a large project into UI and computational parts, then into symbolic and numeric computation, and how package names reflect the directory path to an item.
- Module files versus modules. Distinguish a .py file containing module code from the module inside that file, whose name does not end in .py.
- Package initialization files. Explain the two purposes of an __init__.py file and why it can often be empty.
- Package initialization on first load. Explain that loading mathproj for the first time automatically runs its __init__.py file, which prints the example greeting.
- Package namespace attributes. Describe how the version value assigned in mathproj/__init__.py can be accessed from outside that file through mathproj.
- Explicit submodule loading. Explain why loading mathproj alone does not make mathproj.comp.numeric.n1 available, and how to make its function g available.
- Parent package import sequence. Trace what Python imports before mathproj.comp.numeric.n1 and explain the Hello lines printed during that import.
- Explicit imports between package files. Explain why code in one package file must import objects defined in another, using n1.py’s imports of version and h as examples.
- Relative import paths and limits. Describe how a relative import can refer to a module in the same directory or move up the package hierarchy, and explain why a module run as the main module cannot use relative imports.
- Wildcard import name selection. Explain how a package’s __all__ attribute controls which names a from ... import * statement imports, and what happens when __all__ is absent.
- Nonrecursive wildcard imports. Describe why importing * from mathproj does not also load names listed by the comp subpackage’s __all__.
- Shallow package structure. Justify a shallow directory layout for most packages, while allowing that unusually large collections of code may need more levels.
- Consistent import name visibility. Explain why the section discourages using __all__ solely to hide names from from ... import * and identify the suggested alternative.
- Text Processing Package Refactor. Describe how to refactor the earlier text-cleaning and word-frequency-counting code into a package, separating cleaning from processing and writing a simple main function that uses all its modules.
- Division of function responsibilities. Explain that the lab’s main concern is deciding what to put in each function, and that different divisions are acceptable unless one is obviously wrong.
What it builds on
- What is a module? (chapter 10) · The idea of a Python module as a unit of reusable code.
- Private names in modules (chapter 10) · Draws on the earlier discussion of underscore-prefixed names in modules.
- Creating a module (chapter 10) · The earlier module containing the text-cleaning and word-frequency-counting code.
- Adding exceptions (chapter 14) · The error handling added to that code.
Section by section
Introduction
This introduction explains why packages help organize related modules and distinguishes that meaning of package from a distributable file.
- Packages for related modules: Explain why a growing project may benefit from combining related modules into a package rather than keeping one giant module or many unconnected modules.
Why it matters: This gives you the organizing problem that packages are meant to solve. - Package meanings in this chapter: Distinguish the chapter’s meaning of a Python package from the distributable-file meaning mentioned in the note.
Why it matters: Keeping the two meanings separate clarifies what this chapter covers.
Key terms: module, package, distributable file
18.1 What is a package?
Defines packages by contrasting their directory-based structure and purpose with modules.
- Package and module structure: Distinguish a module from a package by describing what each contains and how each gets its name.
Why it matters: The file-versus-directory distinction makes the package concept easier to grasp. - Packages for larger projects: Explain why packages extend the idea of modules for large projects.
Why it matters: This explains why related code might be organized into a package rather than a single module.
Key terms: module, package, main package directory, modules, packages, related modules
18.2 A first example: mathproj
The mathproj example shows how a large Python project can be divided into directories and files whose structure helps organize and name its code.
- Package hierarchy and names: Explain how mathproj divides a large project into UI and computational parts, then into symbolic and numeric computation, and how package names reflect the directory path to an item.
Why it matters: The hierarchy makes a large codebase easier to keep organized and its contents easier to identify. - Distinct symbolic and numeric pi: Distinguish the two uses of pi in the example: separate constants.py files provide a numeric variant and a symbolic variant, with symbolic pi represented by an abstract Python object.
Why it matters: The two versions of pi make the benefit of organizing and naming code by its location concrete.
Key terms: package, mathproj, ui, comp, symbolic, numeric, constants.py, pi, PiClass
18.3 Implementing the mathproj package
The section introduces the mathproj example and explains how to prepare its files and a clean Python session for the chapter’s examples.
- Module files versus modules: Distinguish a .py file containing module code from the module inside that file, whose name does not end in .py.
Why it matters: That distinction helps you talk clearly about the files and modules in mathproj. - Mathproj example setup: Describe how to prepare the mathproj files for the examples in Colaboratory or another Python installation, including where to set the current working directory.
Why it matters: The examples depend on the mathproj files being in the expected location. - Clean sessions for examples: Explain why each separate chapter example should run in a new Python session and how to do that in Colaboratory.
Why it matters: Restarting prevents earlier imports from changing what a later example demonstrates.
Key terms: file, .py, module, mathproj, Colaboratory, setup cell, current working directory, Python namespace, import statements, new session
18.3.1 __init__.py files in packages
This subsection explains how __init__.py files mark package directories and provide a place for initialization code.
- Package initialization files: Explain the two purposes of an __init__.py file and why it can often be empty.
Why it matters: It explains why package directories often contain a file that appears to do nothing.
Key terms: __init__.py, package, subpackage, package initialization
18.3.2 Basic use of the mathproj package
Shows what happens when mathproj is first loaded and how to access an attribute defined in the package.
- Package initialization on first load: Explain that loading mathproj for the first time automatically runs its __init__.py file, which prints the example greeting.
Why it matters: It explains why loading mathproj prints a message without a separate call to __init__.py. - Package namespace attributes: Describe how the version value assigned in mathproj/__init__.py can be accessed from outside that file through mathproj.
Why it matters: It shows how code using a package can reach a value the package defines.
Key terms: mathproj/__init__.py, package, first loaded, version, mathproj package namespace, attributes
18.3.3 Loading subpackages and submodules
This subsection shows that importing a package does not load all its submodules, and that importing a nested module loads its parent packages first.
- Explicit submodule loading: Explain why loading mathproj alone does not make mathproj.comp.numeric.n1 available, and how to make its function g available.
Why it matters: It helps you understand why importing a package may not yet give you access to a nested module. - Parent package import sequence: Trace what Python imports before mathproj.comp.numeric.n1 and explain the Hello lines printed during that import.
Why it matters: It explains why importing one nested module can visibly execute code in its parent packages.
Key terms: top-level module, submodules, mathproj.comp.numeric.n1, subpackage, __init__.py, mathproj.comp, mathproj.comp.numeric
18.3.4 import statements within packages
This subsection explains how modules inside a package import objects from other package files, including when relative imports work.
- Explicit imports between package files: Explain why code in one package file must import objects defined in another, using n1.py’s imports of version and h as examples.
Why it matters: This explains why package modules still need their own import statements. - Relative import paths and limits: Describe how a relative import can refer to a module in the same directory or move up the package hierarchy, and explain why a module run as the main module cannot use relative imports.
Why it matters: This helps you choose a package import that works in the module’s intended execution context.
Key terms: package, module, import statements, version, h, relative import, package hierarchy, __name__, __main__
18.4 The __all__ attribute
This section explains how __all__ controls wildcard imports from a package, why explicit names matter, and why wildcard imports do not recurse into subpackages.
- Wildcard import name selection: Explain how a package’s __all__ attribute controls which names a from ... import * statement imports, and what happens when __all__ is absent.
Why it matters: It lets you predict which names a wildcard import will make available. - Explicit names avoid case ambiguity: Explain why the section uses an explicit __all__ list rather than relying on filenames to determine wildcard-import names.
Why it matters: It explains why wildcard imports use an explicit name list instead of guessing names from the filesystem. - Nonrecursive wildcard imports: Describe why importing * from mathproj does not also load names listed by the comp subpackage’s __all__.
Why it matters: It prevents you from assuming that importing a package also exposes every name inside its subpackages.
Key terms: __all__, __init__.py, from ... import *, case, subpackages, mathproj, comp, c1, recursive importing
18.5 Proper use of packages
This section recommends keeping package layouts shallow and avoiding inconsistent use of __all__ to hide names.
- Shallow package structure: Justify a shallow directory layout for most packages, while allowing that unusually large collections of code may need more levels.
Why it matters: A package can be simpler than the mechanism allows. - Consistent import name visibility: Explain why the section discourages using __all__ solely to hide names from from ... import * and identify the suggested alternative.
Why it matters: The choice of how to hide a name affects how consistently a package presents its contents.
Key terms: packages, deeply nested directory structures, top-level directory, Flat is better than nested, __all__ attribute, from ... import*, underscore
18.6 Creating a package
This section asks the reader to turn an earlier text-processing module into a package with separate responsibilities and a main function that uses its modules.
- Text Processing Package Refactor: Describe how to refactor the earlier text-cleaning and word-frequency-counting code into a package, separating cleaning from processing and writing a simple main function that uses all its modules.
Why it matters: Separating the existing code by responsibility gives the package a clear structure to use from a main function.
Key terms: package, module, cleaning functions, processing functions, custom exceptions, main function
18.6.1 Solving the problem with AI-generated code
This subsection says the lab’s main challenge is dividing work among functions and allows more than one reasonable division.
- Division of function responsibilities: Explain that the lab’s main concern is deciding what to put in each function, and that different divisions are acceptable unless one is obviously wrong.
Why it matters: You can focus on making a sensible division rather than matching one exact solution.
Key terms: function, division
18.6.2 Solutions and discussion
18.7 Summary
Concepts explained in this chapter
- packageA package is a structured collection of software modules organized under a common namespace.
- subpackageA subpackage is a package nested within another package's directory structure.
- moduleA module is a distinct, bounded software unit that groups related code or functionality and provides an organizational or interaction boundary within a larger system.
- constructor method__init__ is Python's conventional instance-initialization method invoked when an object is initialized.
- user-defined exceptionA custom exception is a programmer-defined exception class representing an application-specific abnormal condition.
- word countA word count is the number of words identified in a text according to a specified word-segmentation rule.
- __init__.pyAn __init__.py file is a Python package file that can contain initialization code executed when the package is imported.
- __all____all__ is a module-level sequence that specifies the names exported by a wildcard import.
- directory structureA directory is a filesystem container that organizes files and other directories under a named location.
- wildcard importA wildcard import is an import statement form that brings the names exported by a module directly into the importing namespace.
- import statementAn import statement is a source-code declaration that makes names or definitions from another module, package, or namespace available in the current scope.
- namespaceA global namespace is the namespace associated with a module, script, or interactive session and visible as the defining environment for its functions.
- entry pointA main function, or controlling function, is a conventionally designated function that coordinates a program's execution and serves as its controlling entry point.
- refactoringRefactoring is the systematic restructuring of existing source code to improve its internal qualities without changing its externally observable behavior.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
