Python Modules and Packages: The Complete Beginner's Guide
This chapter shows how to scale beyond one file using modules and packages, master import patterns, use the standard library, install third-party packages with pip, and isolate dependencies with virtual environments.
Chapter 13 of 20 · Intermediate · 40 min · Python Programming Course
Every Python program you have written so far can live in one file. Real projects cannot. Modules and packages let you split code into reusable, maintainable pieces.
What You Will Learn in This Chapter
By the end of this tutorial you will be able to:
- Import modules with
import,from ... import, and aliases - Use core standard-library modules effectively
- Create and import your own modules
- Understand module lookup using
sys.path - Build packages with
__init__.py - Install third-party packages with
pip - Use virtual environments for dependency isolation
- Avoid common import pitfalls
Estimated time: 40 minutes reading + 20 minutes practice
The Three Import Styles
1) import module
import math
print(math.sqrt(16))
2) from module import name
from math import sqrt, pi
print(sqrt(25), pi)
3) import module as alias
import datetime as dt
print(dt.date.today())
Avoid wildcard imports:
from math import * # avoid
Standard Library Essentials
math
import math
print(math.pi, math.sqrt(144), math.factorial(6))
random
import random
random.seed(42)
print(random.randint(1, 100))
datetime
from datetime import datetime, timedelta
now = datetime.now()
print(now + timedelta(days=1))
os
import os
print(os.getcwd())
print(os.path.join("data", "output.csv"))
sys
import sys
print(sys.version)
print(sys.path)
json
import json
data = {"name": "Alice", "age": 25}
raw = json.dumps(data, indent=2)
print(json.loads(raw)["name"])
collections
from collections import Counter, defaultdict, deque
print(Counter(["python", "python", "java"]))
Creating Your Own Module
Any .py file is a module.
mathutils.py:
PI = 3.14159265358979
def circle_area(radius):
return PI * radius ** 2
main.py:
import mathutils
print(mathutils.circle_area(5))
How Python Finds Modules
Python searches import locations in order including current directory, environment paths, stdlib, and site-packages.
import sys
print(sys.path)
__name__ Guard
def circle_area(radius):
return 3.14159 * radius ** 2
if __name__ == "__main__":
print(circle_area(5))
Creating Packages
A package is a directory with modules and typically __init__.py.
# schoolabe/__init__.py
from .math_tools import add
__version__ = "1.0.0"
Installing Third-Party Packages with pip
pip install requests
pip freeze > requirements.txt
pip install -r requirements.txt
Virtual Environments
python -m venv venv
source venv/bin/activate
pip install requests
deactivate
Use a virtual environment per project to prevent version conflicts.
A Complete Working Program
Below is a compact report utility that combines standard library modules:
import math
import json
import random
from datetime import datetime, timedelta
from collections import Counter, defaultdict
random.seed(42)
def generate_sales_data(n=20):
products = ["Python", "Kafka", "DSA", "JS"]
records = []
start = datetime(2026, 1, 1)
for i in range(n):
records.append({
"id": f"TXN{i+1:04d}",
"date": (start + timedelta(days=random.randint(0, 60))).strftime("%Y-%m-%d"),
"product": random.choice(products),
"amount": round(random.uniform(30, 300), 2),
})
return records
def report(records):
amounts = [r["amount"] for r in records]
totals = defaultdict(float)
for r in records:
totals[r["product"]] += r["amount"]
return {
"generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"count": len(records),
"total": round(sum(amounts), 2),
"mean": round(sum(amounts) / len(amounts), 2),
"std_dev": round(math.sqrt(sum((x - (sum(amounts)/len(amounts)))**2 for x in amounts)/len(amounts)), 2),
"top_products": Counter(r["product"] for r in records).most_common(3),
"revenue_by_product": dict(sorted(totals.items(), key=lambda x: x[1], reverse=True)),
}
5 Import Mistakes Every Beginner Makes
- Naming your file like stdlib modules (
random.py,json.py) - Circular imports between modules
- Wildcard imports (
from x import *) - Missing or misusing package
__init__.py - Installing globally instead of inside a virtual environment
Practice Exercises
- Standard library mini report with
math,random,datetime - Build a custom
text_utils.pyand import it frommain.py - JSON write/read workflow for student records
os-based Python file listing utilitycollectionsusage withCounter,defaultdict,deque
-> See all Python practice exercises with solutions
What Comes Next - Day 14: File Handling
Day 14 moves from code organisation to persistent data:
- opening/closing files safely
- reading/writing/appending content
- CSV handling
- path operations with pathlib
- safe file error handling
-> Continue to Day 14: File Handling
Frequently Asked Questions
What is a module in Python?
A module is any .py file containing Python code.
What is the difference between a module and a package?
A module is one file. A package is a directory of modules.
What is the difference between import styles?
import x keeps namespace explicit, from x import y imports directly, aliases shorten long names.
What does if __name__ == "__main__" mean?
It runs code only when the file is executed directly, not when imported.
What is pip in Python?
pip installs Python packages from PyPI and manages dependencies.
What is a virtual environment in Python?
An isolated environment for project-specific dependencies.
What is __init__.py in a package?
A package initializer that marks package boundaries and controls exports.
What is sys.path in Python?
A list of paths Python searches during import resolution.
Chapter navigation
- Previous: Day 12: Inheritance and Polymorphism
- Next: Day 14: File Handling
- Python Quiz: Take the Python quiz
- All Python exercises: Explore Python exercises
Controlling Package Exports with __all__
__all__ is a list of public names exported when someone uses from mypackage import *. It also signals to developers what the intended public API is.
# mylib/utils.py
__all__ = ['format_date', 'parse_date'] # Only these are "public"
def format_date(d): ...
def parse_date(s): ...
def _internal_helper(): ... # Leading underscore = private convention
Essential Standard Library Deep Dive
collections — Specialized Data Structures
from collections import Counter, defaultdict, deque, namedtuple, OrderedDict
# Counter: count occurrences
words = "apple banana apple cherry banana apple".split()
freq = Counter(words)
print(freq) # Counter({'apple': 3, 'banana': 2, 'cherry': 1})
print(freq.most_common(2)) # [('apple', 3), ('banana', 2)]
freq.update(['apple', 'date'])
# defaultdict: auto-initialize missing keys
from collections import defaultdict
graph = defaultdict(list)
graph['A'].append('B') # No KeyError — creates [] automatically
graph['A'].append('C')
# deque: O(1) append/pop from both ends
from collections import deque
queue = deque(['a', 'b', 'c'], maxlen=5)
queue.appendleft('z') # O(1) — unlike list insert at 0
queue.append('d')
queue.rotate(1) # Rotate right by 1
# namedtuple: lightweight record type
Point = namedtuple('Point', ['x', 'y'])
p = Point(3, 4)
print(p.x, p.y) # 3 4
print(p._asdict()) # {'x': 3, 'y': 4}
itertools — Combinatorial and Infinite Iterators
import itertools
# Infinite iterators
for i in itertools.count(10, 2): # 10, 12, 14, ...
if i > 20: break
# Combinatorics
list(itertools.combinations('ABC', 2)) # [('A','B'), ('A','C'), ('B','C')]
list(itertools.permutations('ABC', 2)) # All 2-permutations
list(itertools.product([0,1], repeat=3)) # All 3-bit binary strings
# Chaining and grouping
list(itertools.chain([1,2], [3,4], [5])) # [1, 2, 3, 4, 5]
for key, group in itertools.groupby("AAABBBCCAA"):
print(key, list(group)) # A [A,A,A], B [B,B,B], C [C,C], A [A,A]
# accumulate — running totals
list(itertools.accumulate([1,2,3,4,5])) # [1, 3, 6, 10, 15]
list(itertools.accumulate([1,2,3,4,5], max)) # [1, 2, 3, 4, 5]
functools — Higher-Order Functions
import functools
# lru_cache — memoize function results
@functools.lru_cache(maxsize=128)
def fibonacci(n):
if n < 2: return n
return fibonacci(n-1) + fibonacci(n-2)
fibonacci(100) # Instant with caching
# partial — pre-fill arguments
from functools import partial
double = partial(pow, exp=2)
triple_add = partial(lambda a, b, c: a+b+c, c=0) # Fix last arg
# reduce — fold a sequence
product = functools.reduce(lambda a, b: a * b, [1, 2, 3, 4, 5]) # 120
pathlib — Modern Path Operations
from pathlib import Path
# All path operations are object methods — no string concatenation
home = Path.home()
project = home / 'projects' / 'myapp' # / operator joins paths!
config = project / 'config.json'
print(config.exists()) # True/False
print(config.stem) # 'config'
print(config.suffix) # '.json'
print(config.parent) # ~/projects/myapp
# Glob patterns
for py_file in project.glob('**/*.py'): # Recursive
print(py_file.name)
# Read/write
config.write_text('{"debug": true}')
data = config.read_text()
Relative Imports in Packages
Within a package, prefer relative imports over absolute ones for internal modules:
# myapp/utils/validators.py
from ..models import User # Two levels up, then into models
from .helpers import format_email # Same package (utils)
from myapp.config import SETTINGS # Absolute import — also fine
Rule: Relative imports only work inside packages (not scripts run directly).
datetime and time — Working with Dates
from datetime import datetime, date, timedelta, timezone
# Current time
now = datetime.now() # Local time (no timezone info)
utc_now = datetime.now(tz=timezone.utc) # UTC with timezone info
# Creating dates
birthday = date(1995, 6, 15)
meeting = datetime(2026, 7, 1, 14, 30, 0)
# Arithmetic
tomorrow = date.today() + timedelta(days=1)
two_weeks = now + timedelta(weeks=2)
diff = datetime(2026, 12, 31) - now # timedelta object
print(f"{diff.days} days until year end")
# Formatting and parsing
formatted = now.strftime("%Y-%m-%d %H:%M:%S") # "2026-06-22 10:30:00"
parsed = datetime.strptime("22/06/2026", "%d/%m/%Y")
# ISO format (recommended for APIs/files)
iso = now.isoformat() # "2026-06-22T10:30:00"
back = datetime.fromisoformat(iso)
json — Serialization Patterns
import json
from datetime import datetime
# Custom encoder for types json doesn't know about
class AppEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
if isinstance(obj, set):
return sorted(obj) # Sets → sorted lists
if hasattr(obj, '__dict__'):
return obj.__dict__ # Simple objects → dict
return super().default(obj)
data = {"created": datetime.now(), "tags": {"python", "web"}}
serialized = json.dumps(data, cls=AppEncoder, indent=2)
# Read/write files safely
def load_json(path, default=None):
try:
with open(path, encoding='utf-8') as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
return default
def save_json(path, data, indent=2):
with open(path, 'w', encoding='utf-8') as f:
json.dump(data, f, cls=AppEncoder, indent=indent, ensure_ascii=False)
os and sys — System Interaction
import os, sys
# Environment variables
db_url = os.environ.get('DATABASE_URL', 'sqlite:///local.db')
debug = os.getenv('DEBUG', 'false').lower() == 'true'
# Process info
pid = os.getpid()
cwd = os.getcwd()
# sys — interpreter info
print(sys.version) # Python version string
print(sys.platform) # 'linux', 'darwin', 'win32'
print(sys.argv) # Command-line arguments: [script_name, arg1, arg2, ...]
# sys.path manipulation (use sparingly)
sys.path.insert(0, '/path/to/custom/lib') # Add at front for priority
# Exit with status code
if not os.path.exists('required_config.json'):
print("ERROR: required_config.json missing", file=sys.stderr)
sys.exit(1) # Non-zero = failure
Creating a Professional Python Package
my_library/
├── pyproject.toml ← Modern package config (replaces setup.py)
├── README.md
├── src/
│ └── my_library/
│ ├── __init__.py ← Public API
│ ├── core.py
│ ├── utils.py
│ └── _internal.py ← Private (underscore prefix)
└── tests/
└── test_core.py
# pyproject.toml (minimal)
[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.backends.legacy:build"
[project]
name = "my-library"
version = "1.0.0"
description = "A short description"
requires-python = ">=3.10"
dependencies = ["requests>=2.28"]
[project.optional-dependencies]
dev = ["pytest", "black", "mypy"]
# src/my_library/__init__.py
"""Expose only the public API."""
from .core import MyClass, important_function
from .utils import helper
__all__ = ['MyClass', 'important_function', 'helper']
__version__ = '1.0.0'
# Install locally in editable mode for development
pip install -e ".[dev]"
# Build distribution
pip install build
python -m build # Creates dist/*.whl and dist/*.tar.gz
# Publish to PyPI
pip install twine
twine upload dist/*
Summary Cheatsheet
| Task | Tool |
|---|---|
| Import module | `import math` / `from math import sqrt` |
| Create package | Directory + `__init__.py` |
| Install package | `pip install requests` |
| Isolated environment | `python -m venv .venv` |
| Counting items | `collections.Counter` |
| Auto-init missing keys | `collections.defaultdict` |
| Double-ended queue | `collections.deque` |
| Memoize function | `@functools.lru_cache` |
| Generate combinations | `itertools.combinations` |
| Modern file paths | `pathlib.Path` |
| Date arithmetic | `datetime.timedelta` |
| JSON read/write | `json.load` / `json.dump` |
| Env variable | `os.environ.get('KEY', 'default')` |
| Script args | `sys.argv` |
Frequently asked questions: Modules and packages
What is a module in Python?
A module is any .py file containing Python code such as functions, classes, and variables.
What is the difference between a module and a package?
A module is a single file. A package is a directory containing related modules.
What is the difference between import math and from math import sqrt?
import math keeps namespaced access (math.sqrt). from math import sqrt imports a specific name directly (sqrt).
What does if __name__ == "__main__": mean in Python?
It runs code only when a file is executed directly, not when imported as a module.
What is pip in Python?
pip is Python’s package installer used to install, upgrade, and manage third-party packages.
What is a virtual environment in Python?
A virtual environment isolates project dependencies so package versions do not conflict across projects.
What is __init__.py in a Python package?
__init__.py marks package boundaries and can control which names are exported at package level.
What is sys.path in Python?
sys.path lists directories Python searches when resolving imports.
Related Page
Day 12: Inheritance and Polymorphism
Learn about day 12: inheritance and polymorphism
Learn MoreDay 14: File Handling
Continue with day 14: file handling
Learn MorePython exercises hub
165+ programs with solutions
Learn MorePython quiz
Multiple-choice checks with explanations
Learn MoreBasic Python programs
Modules and package practice programs
Learn More