Last 30 Days
No notifications
A module is any .py file. A package is a directory of modules containing an __init__.py file. Python's module system lets you organize code, reuse logic, and leverage a massive ecosystem of third-party libraries.
import math # Full module import
from math import sqrt, pi # Specific names
from os.path import join as pjoin # Alias
import numpy as np # Conventional alias__name__ Guardif __name__ == "__main__":
main() # Runs only when executed directlyThis prevents code from running when your module is imported by another script — essential for reusable modules.
mypackage/
├── __init__.py # Makes it a package
├── utils.py
└── core/
├── __init__.py
└── engine.py| Command | Purpose | |||
python -m venv .venv | Create virtual environment | |||
.venv/Scripts/activate | Activate (Windows) | |||
pip install requests | Install a package | |||
pip freeze > requirements.txt | Save dependencies | |||
pip install -r requirements.txt | Restore dependencies | Popular Standard Library Modules | Module | Purpose |
os / sys | OS interaction, system args | |||
math / random | Math ops, random generation | |||
datetime | Dates & times | |||
collections | Counter, defaultdict, deque | |||
itertools | Combinatoric iterators | |||
json | JSON encode/decode | |||
pathlib | Object-oriented file paths | |||
functools | Higher-order functions (lru_cache, reduce) |
> Tip: Always use virtual environments per project to avoid dependency conflicts. Keep requirements.txt version-pinned for reproducibility.
As soon as your code grows past a few hundred lines, you'll want to split it across files. Modules and packages are how Python does that — plus they're how you reach for the enormous third-party ecosystem (FastAPI, NumPy, requests, anything you'll pip install).
A module is just a .py file. Any Python file you can run is also one you can import. When you import it, Python runs the file once, then exposes its names (functions, classes, variables) under the module's namespace.
# math_utils.py
PI = 3.14159
def square(x): return x * x# main.py
import math_utils
print(math_utils.PI, math_utils.square(4))
import json # whole module
import numpy as np # alias
from math import pi, sqrt # specific names
from collections import defaultdict # one name from a package
from .helpers import clean # relative (inside a package)Avoid from module import * — it pollutes your namespace and makes refactors painful.
A package is a *folder* containing modules and an __init__.py (which can be empty):
myapp/
├── __init__.py
├── main.py
└── utils/
├── __init__.py
├── strings.py
└── numbers.pyNow from myapp.utils.strings import slugify works.
python -m venv .venv
.venv\Scripts\activate # Windows
source .venv/bin/activate # mac/linuxpip install requests pandas
pip freeze > requirements.txt
pip install -r requirements.txt
Always work inside a venv. It isolates project dependencies so projects don't poison each other. Modern alternatives: uv (fast), poetry, pipx for CLI tools.
1. Naming a file random.py / json.py / os.py. Shadows the standard library; import random will pick *your* file and break.
2. from x import *. Hides the source of names; trips linters and humans.
3. Forgetting __init__.py in folders you intend as packages (in some scenarios still required, especially with editable installs).
4. Installing globally. Always activate a venv first or use pipx for CLI tools.
5. Mixing pip and pip3 randomly. Use python -m pip install ... — it always uses the active interpreter.
6. Top-level side effects. Code at module top-level runs on import. Wrap scripts in if __name__ == '__main__':.
sys.pathWhen you write import foo, Python checks:
1. Built-in modules baked into the interpreter (sys, builtins).
2. Each entry of sys.path, in order:
- The directory of the running script (or current dir for the REPL).
- PYTHONPATH env var.
- The active interpreter's site-packages (where pip install puts things).
Inspect with import sys; print(sys.path). After import, the module is cached in sys.modules so further imports are free.
# absolute (preferred)
from myapp.utils.strings import slugify# relative (only inside a package)
from .strings import slugify
from ..config import settings
Absolute imports survive moving the file. Relative imports are concise inside large packages but fragile if you move things — prefer absolute in application code.
if __name__ == '__main__'def main():
...if __name__ == '__main__':
main()
Lets the file work as a runnable script (python file.py) and as an importable module (from file import main) without auto-executing the body.
collections — Counter, defaultdict, deque, namedtuple, ChainMap.itertools — chain, product, combinations, groupby, islice.functools — lru_cache, partial, reduce, wraps.pathlib, os, shutil — paths and filesystem.datetime, time, zoneinfo — dates and times.json, csv, re, urllib — data and text.logging, argparse, subprocess — tooling.statistics, math, random, decimal, fractions — numbers.unittest, asyncio, typing — testing, async, type hints.pip installs by knowing what's already shipped.myapp/
├── pyproject.toml # metadata + deps + build
├── README.md
├── src/
│ └── myapp/
│ ├── __init__.py # public API
│ ├── _internal.py # leading _ = private
│ └── cli.py
└── tests/In __init__.py re-export the public API and define __all__:
from .core import Engine, run
__all__ = ["Engine", "run"]Users do from myapp import Engine; internals stay tidy. Names with leading underscores are convention for "don't import this".
pyproject.tomlFor your own packages during development:
pip install -e .With pyproject.toml:
[project]
name = "myapp"
version = "0.1.0"
dependencies = ["requests>=2.31", "pandas"][project.scripts]
myapp = "myapp.cli:main"
Now your code is importable from anywhere in the venv, and myapp becomes a real CLI command.
If module A imports B and B imports A, you'll often hit ImportError or get half-initialised modules. Fixes:
import other *inside* the function that uses it.if typing.TYPE_CHECKING: from x import Y — imports at static-check time only.requirements.txt lists *what* you want; lockfiles (pip-tools, poetry.lock, uv.lock) pin *exact* versions and hashes — use them for production.pipx (each tool gets its own venv).1. Create a venv, install requests, write a 10-line script that fetches a URL and prints the status code, and freeze the deps to requirements.txt.
2. Split a single script.py (with helpers + main loop) into a tiny package myapp/ with utils.py and a cli.py entry point.
3. Add a pyproject.toml and pip install -e . your package; run it from any directory.
4. Demonstrate a circular import and fix it by moving the shared piece to a third module.