Beginner
10 min read
#Python#Modules#Packages#pip#Virtual Environments

Modules & Packages — The Complete Notebook

Comprehensive guide on Modules & Packages — The Complete Notebook.

Modules & Packages

1. Overview#

As a program grows past a single file, you need a way to organize code into reusable units. Python calls a single .py file a module, and a directory of related modules a package. This note also covers pip and virtual environments — the tools that manage which packages are installed and where.


2. Modules#

2.1 Creating and Importing a Module#

🐍 Python
# file: math_utils.py def add(a, b): return a + b def multiply(a, b): return a * b PI = 3.14159
🐍 Python
# file: main.py import math_utils print(math_utils.add(2, 3)) # 5 print(math_utils.PI) # 3.14159

2.2 Import Variants#

🐍 Python
import math_utils # import whole module, access via math_utils.add() from math_utils import add # import a specific name directly from math_utils import add, multiply # import multiple names from math_utils import add as sum_two # rename on import import math_utils as mu # rename the module itself from math_utils import * # import everything (avoid — pollutes namespace)

Avoid from module import * in real projects — it makes it unclear where a name came from and can silently overwrite existing names.

2.3 if __name__ == "__main__":#

This lets a file work both as a standalone script and as an importable module.

🐍 Python
# file: math_utils.py def add(a, b): return a + b if __name__ == "__main__": # Only runs when this file is executed directly (python math_utils.py) # Does NOT run when imported elsewhere print(add(2, 3))

2.4 Module Search Path#

Python looks for modules in this order: the current script's directory → PYTHONPATH env variable → the standard library → installed site-packages.

🐍 Python
import sys print(sys.path) # list of directories Python searches, in order

3. Packages#

3.1 Basic Package Structure#

code
my_project/ ├── main.py └── utils/ ├── __init__.py ├── math_ops.py └── string_ops.py
🐍 Python
# utils/math_ops.py def add(a, b): return a + b
🐍 Python
# main.py from utils.math_ops import add print(add(2, 3))

3.2 The Role of __init__.py#

An __init__.py file marks a directory as a package and runs when the package is first imported. It can also be used to control what's exposed at the package level.

🐍 Python
# utils/__init__.py from .math_ops import add from .string_ops import capitalize_words # Now callers can do: # from utils import add, capitalize_words # instead of: # from utils.math_ops import add

Since Python 3.3, __init__.py is technically optional for "namespace packages," but it's still standard practice to include one — it makes the package's public API explicit and works reliably everywhere.

3.3 Relative vs Absolute Imports#

🐍 Python
# Absolute import — preferred, unambiguous from utils.math_ops import add # Relative import — used *inside* a package, relative to the current module from .math_ops import add # same package from ..shared import constants # parent package

4. Virtual Environments#

A virtual environment is an isolated Python installation for a single project — so its dependencies don't clash with other projects or the system Python.

4.1 Creating and Activating#

bash
# Create a virtual environment named "venv" python -m venv venv # Activate it source venv/bin/activate # macOS/Linux venv\Scripts\activate # Windows # Deactivate when done deactivate

Once activated, python and pip point to the environment's isolated copies — packages installed here won't affect your global Python installation.

4.2 Confirming You're in the Right Environment#

bash
which python # macOS/Linux — should point inside venv/ python -m pip --version

5. pip — The Package Installer#

5.1 Common Commands#

bash
pip install requests # install a package pip install requests==2.31.0 # install a specific version pip install --upgrade requests # upgrade an installed package pip uninstall requests # remove a package pip list # list installed packages pip show requests # details about one package

5.2 requirements.txt — Reproducible Environments#

bash
pip freeze > requirements.txt # snapshot current environment pip install -r requirements.txt # recreate it elsewhere
Mathematical Formulation
# requirements.txt
requests==2.31.0
fastapi==0.111.0
pydantic>=2.0,<3.0

5.3 Modern Alternative: pyproject.toml#

Newer projects increasingly define dependencies in pyproject.toml (used by tools like poetry and pip itself) instead of requirements.txt. This is covered in detail in packaging-deployment.md.


6. Common Pitfalls#

INCORRECT: Circular Imports#

🐍 Python
# module_a.py from module_b import func_b def func_a(): return func_b()
🐍 Python
# module_b.py from module_a import func_a # ImportError: circular dependency def func_b(): return func_a()

CORRECT: Fix — Restructure or Import Inside the Function#

🐍 Python
# module_b.py def func_b(): from module_a import func_a # deferred import, avoids the circular load at import time return func_a()

INCORRECT: Installing Packages Globally Instead of in a Virtual Environment#

Leads to version conflicts between unrelated projects on the same machine — always work inside an activated virtual environment.


7. Summary & Best Practices Checklist#

  • One virtual environment per project — never install project dependencies globally.
  • Keep requirements.txt (or pyproject.toml) up to date and committed to version control.
  • Use absolute imports for clarity; reserve relative imports for internal package structure.
  • Avoid from module import * in production code.
  • Use __init__.py to define a clean public API for your package.
  • Watch for circular imports when two modules depend on each other — restructure if it happens.
Knowledge Checkpoint

Modules & Package Architecture Checkpoint

Q1.What is the primary purpose of the `if __name__ == '__main__':` boilerplate in a Python script?
ATo allow code inside the block to run only when the script is executed directly, and not when imported as a module.
BTo declare the script as the entry point for CPython bytecode caching.
CTo grant administrator/root permissions to the executing script.
DTo enable multi-threading on the main interpreter thread.
Q2.Where does Python search for imported modules when an `import` statement is executed?
AOnly in the current directory.
BIn the paths listed in `sys.path` (starting with script directory, `PYTHONPATH`, and installed site-packages).
CIn the Windows Registry or `/etc/python` directories.
DIn the active user's Desktop folder.
Q3.What is the purpose of `__all__` defined in a package's `__init__.py`?
AIt restricts which users can read the file.
BIt defines the public symbols exported when a consumer uses `from module import *`.
CIt deletes private variables from memory after import.
DIt automatically compiles the module into a `.so` binary.
Track Your Learning

Finished studying this notebook?

Mark this guide as completed to update your course progress roadmap.