Lesson 13 - Modules and Packages
On this page
- Introduction
- What Are Modules?
- Creating Your First Module
- Different Ways to Import
- How Python Finds Modules
- Module Initialization
- The
if __name__ == "__main__"Pattern - Packages: Organizing Multiple Modules
- The Standard Library
- Third-Party Modules
- Practical Example: Building a Bookstore System
- Best Practices for Modules and Packages
- Summary
Introduction
As your Python programs grow larger, keeping all your code in a single file becomes unwieldy and difficult to maintain. Modules are Python’s solution for organizing code into separate, reusable files. They allow you to break down complex programs into manageable pieces and share code across multiple projects.
In this lesson, we’ll explore:
- What modules are and why they’re essential
- How to create your own modules
- Different ways to import modules
- Understanding Python packages
- The standard library and third-party modules
- Best practices for organizing code
What Are Modules?
A module is simply a Python file (.py) that contains Python code—functions, classes, variables, or any executable statements. When you import a module, you gain access to everything defined in that file.
Think of modules as toolboxes:
- Each module contains related tools (functions, classes)
- You can use these tools in multiple projects
- You don’t need to know how the tools work internally
- You can organize tools into different boxes for different purposes
Why Use Modules?
1. Code Organization
Instead of one massive file with thousands of lines:
# Bad: Everything in one file (main.py)
def calculate_discount(price, discount_percent):
# ... code ...
pass
def format_currency(amount):
# ... code ...
pass
def validate_isbn(isbn):
# ... code ...
pass
class Book:
# ... 100 lines of code ...
pass
class ShoppingCart:
# ... 150 lines of code ...
pass
# ... hundreds more lines ...Break it into logical modules:
bookstore/
pricing.py # calculate_discount, format_currency
validation.py # validate_isbn, validate_email
models.py # Book, ShoppingCart classes
main.py # Main program2. Code Reusability
Write a function once, use it in multiple projects:
# utils.py - Can be used across projects
def format_currency(amount):
return f"${amount:.2f}"
# project1/main.py
from utils import format_currency
print(format_currency(29.99))
# project2/report.py
from utils import format_currency
total = format_currency(1250.50)3. Namespace Management
Modules prevent name conflicts:
# Without modules - names can conflict
def read(): # Which read? File read? Database read?
pass
# With modules - clear separation
import file_utils
import database
file_utils.read() # Clearly reads a file
database.read() # Clearly reads from database4. Maintainability
- Easier to find and fix bugs in smaller files
- Multiple developers can work on different modules
- Changes in one module don’t affect others (if interfaces stay the same)
- Easier to test individual components
Creating Your First Module
Let’s create a simple module for book-related utilities.
Step 1: Create a file named book_utils.py:
# book_utils.py
def format_title(title):
"""Format a book title in title case."""
return title.title()
def calculate_discount(price, discount_percent):
"""Calculate discounted price."""
discount_amount = price * (discount_percent / 100)
return price - discount_amount
def format_isbn(isbn):
"""Format ISBN with hyphens."""
# Remove existing hyphens/spaces
clean = isbn.replace('-', '').replace(' ', '')
# Format as ISBN-13: 978-0-123-45678-9
if len(clean) == 13:
return f"{clean[0:3]}-{clean[3]}-{clean[4:7]}-{clean[7:12]}-{clean[12]}"
return isbn
# Module-level variable
DEFAULT_DISCOUNT = 10Step 2: Use the module in another file:
# main.py
import book_utils
# Use functions from the module
title = book_utils.format_title("python crash course")
print(f"Title: {title}") # Title: Python Crash Course
price = 49.99
discounted = book_utils.calculate_discount(price, 20)
print(f"Price: ${discounted:.2f}") # Price: $39.99
isbn = book_utils.format_isbn("9780123456789")
print(f"ISBN: {isbn}") # ISBN: 978-0-123-45678-9
# Access module variable
print(f"Default discount: {book_utils.DEFAULT_DISCOUNT}%")Output:
Title: Python Crash Course
Price: $39.99
ISBN: 978-0-123-45678-9
Default discount: 10%Different Ways to Import
Python offers several ways to import modules, each with its use case.
1. Import the Entire Module
import book_utils
# Use with module prefix
title = book_utils.format_title("learning python")Pros: Clear where functions come from, no name conflicts Cons: More verbose
2. Import with an Alias
import book_utils as bu
# Shorter prefix
title = bu.format_title("learning python")Pros: Shorter names for frequently used modules Cons: Less clear if alias isn’t obvious
3. Import Specific Items
from book_utils import format_title, calculate_discount
# Use directly without prefix
title = format_title("learning python")
price = calculate_discount(49.99, 15)Pros: Cleaner syntax, less typing Cons: Can cause name conflicts, unclear where functions come from
4. Import Everything (Not Recommended)
from book_utils import *
# All functions available directly
title = format_title("learning python")Pros: Shortest syntax Cons: Can cause unexpected name conflicts, makes code hard to read, unclear what’s imported
Best Practices for Importing
Good:
import datetime
import book_utils
from collections import Counter, defaultdictAvoid:
from datetime import * # Too vague
from book_utils import * # What did we import?How Python Finds Modules
When you import book_utils, Python searches for book_utils.py in this order:
- Current directory where your script is running
- PYTHONPATH environment variable directories
- Standard library directories
- Site-packages directories (third-party modules)
You can see the search path:
import sys
print(sys.path)Module Initialization
When a module is imported for the first time, Python:
- Creates a new namespace for the module
- Executes all the code in the module file
- Creates a module object
- The module is cached in
sys.modules
Important: Module code runs only once (on first import), even if imported multiple times.
# counter_module.py
print("Initializing counter module...")
count = 0
def increment():
global count
count += 1
return count# main.py
import counter_module # Prints: "Initializing counter module..."
import counter_module # Doesn't print again (already imported)
print(counter_module.increment()) # 1
print(counter_module.increment()) # 2The if __name__ == "__main__" Pattern
This special pattern allows a module to act both as:
- An importable module
- A standalone script
# book_utils.py
def format_title(title):
return title.title()
def calculate_discount(price, discount_percent):
discount_amount = price * (discount_percent / 100)
return price - discount_amount
# This code only runs when the file is executed directly
if __name__ == "__main__":
# Test the module functions
print("Testing book_utils module...")
test_title = format_title("python basics")
print(f"Title: {test_title}")
test_price = calculate_discount(50, 20)
print(f"Discounted price: ${test_price:.2f}")
print("All tests passed!")When imported:
import book_utils # Nothing prints (if __name__ block doesn't run)
title = book_utils.format_title("data science")When run directly:
python book_utils.pyOutput:
Testing book_utils module...
Title: Python Basics
Discounted price: $40.00
All tests passed!Why use this pattern?
- Test your module functions during development
- Provide usage examples
- Create dual-purpose scripts
- Keep testing code separate from production code
Packages: Organizing Multiple Modules
A package is a directory containing multiple modules and a special __init__.py file. Packages let you organize related modules into hierarchies.
Creating a Package
bookstore/
__init__.py # Makes it a package (can be empty)
pricing.py # Pricing functions
inventory.py # Inventory management
validation.py # Input validation
models/ # Sub-package for data models
__init__.py
book.py
customer.pyPackage Files
bookstore/pricing.py:
def calculate_discount(price, discount_percent):
discount_amount = price * (discount_percent / 100)
return price - discount_amount
def calculate_tax(price, tax_rate=0.08):
return price * tax_rate
def format_currency(amount):
return f"${amount:.2f}"bookstore/validation.py:
import re
def validate_isbn(isbn):
"""Validate ISBN-13 format."""
clean = isbn.replace('-', '').replace(' ', '')
return len(clean) == 13 and clean.isdigit()
def validate_email(email):
"""Simple email validation."""
pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
return re.match(pattern, email) is not None
def validate_price(price):
"""Validate price is positive."""
try:
price_float = float(price)
return price_float > 0
except ValueError:
return Falsebookstore/models/book.py:
class Book:
def __init__(self, title, author, isbn, price):
self.title = title
self.author = author
self.isbn = isbn
self.price = price
def __str__(self):
return f"{self.title} by {self.author}"
def __repr__(self):
return f"Book('{self.title}', '{self.author}', '{self.isbn}', {self.price})"bookstore/__init__.py:
"""
Bookstore Package
A collection of utilities for bookstore management.
"""
# Make commonly used items available at package level
from .pricing import calculate_discount, format_currency
from .validation import validate_isbn, validate_email
# Package metadata
__version__ = "1.0.0"
__author__ = "Your Name"Using the Package
# Import from package
from bookstore import calculate_discount, format_currency
from bookstore.validation import validate_isbn
from bookstore.models.book import Book
# Use imported items
book = Book("Python Crash Course", "Eric Matthes", "9781593279288", 39.99)
print(book)
if validate_isbn(book.isbn):
discounted_price = calculate_discount(book.price, 20)
print(f"Discounted: {format_currency(discounted_price)}")Output:
Python Crash Course by Eric Matthes
Discounted: $31.99Different Import Styles with Packages
# Import entire sub-module
import bookstore.pricing
price = bookstore.pricing.calculate_discount(50, 10)
# Import sub-module with alias
from bookstore import pricing as pr
price = pr.calculate_discount(50, 10)
# Import specific functions
from bookstore.pricing import calculate_discount, format_currency
# Import from sub-package
from bookstore.models.book import BookThe Standard Library
Python comes with a rich standard library—a collection of modules for common tasks. You don’t need to install these; they’re included with Python.
Common Standard Library Modules
# Math operations
import math
print(math.sqrt(16)) # 4.0
print(math.pi) # 3.141592653589793
# Random numbers
import random
print(random.randint(1, 10)) # Random integer between 1-10
print(random.choice(['apple', 'banana', 'cherry']))
# Date and time
from datetime import datetime, timedelta
now = datetime.now()
tomorrow = now + timedelta(days=1)
print(tomorrow.strftime("%Y-%m-%d"))
# File and directory operations
import os
print(os.getcwd()) # Current working directory
print(os.listdir('.')) # List files in current directory
# JSON data
import json
data = {'title': 'Python Basics', 'price': 29.99}
json_str = json.dumps(data)
print(json_str) # {"title": "Python Basics", "price": 29.99}
# Regular expressions
import re
pattern = r'\d{3}-\d{3}-\d{4}'
phone = "Contact: 555-123-4567"
match = re.search(pattern, phone)
print(match.group()) # 555-123-4567
# Collections
from collections import Counter, defaultdict
words = ['apple', 'banana', 'apple', 'cherry', 'apple']
counter = Counter(words)
print(counter) # Counter({'apple': 3, 'banana': 1, 'cherry': 1})Third-Party Modules
Beyond the standard library, you can install modules created by the Python community using pip (Python’s package installer).
Installing Third-Party Modules
# Install a single package
pip install requests
# Install multiple packages
pip install pandas numpy matplotlib
# Install specific version
pip install django==4.2.0
# Uninstall a package
pip uninstall requests
# List installed packages
pip list
# Show package information
pip show pandasPopular Third-Party Modules
# requests - HTTP library
import requests
response = requests.get('https://api.github.com')
print(response.status_code)
# pandas - Data analysis
import pandas as pd
df = pd.read_csv('books.csv')
print(df.head())
# numpy - Numerical computing
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
print(arr.mean())
# matplotlib - Plotting
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4], [1, 4, 9, 16])
plt.show()Practical Example: Building a Bookstore System
Let’s put it all together with a complete package structure.
Project Structure:
my_bookstore/
bookstore/
__init__.py
book.py
inventory.py
pricing.py
utils.py
main.pybookstore/book.py:
class Book:
"""Represents a book in the bookstore."""
def __init__(self, isbn, title, author, price, quantity=0):
self.isbn = isbn
self.title = title
self.author = author
self.price = price
self.quantity = quantity
def __str__(self):
return f"{self.title} by {self.author} (${self.price:.2f})"
def __repr__(self):
return f"Book('{self.isbn}', '{self.title}', '{self.author}', {self.price}, {self.quantity})"
def is_in_stock(self):
return self.quantity > 0bookstore/pricing.py:
def calculate_discount(price, discount_percent):
"""Calculate price after discount."""
discount_amount = price * (discount_percent / 100)
return price - discount_amount
def calculate_total(items):
"""Calculate total price of multiple items."""
return sum(item['price'] * item['quantity'] for item in items)
def apply_bulk_discount(total, item_count):
"""Apply bulk discount based on number of items."""
if item_count >= 10:
return total * 0.90 # 10% off
elif item_count >= 5:
return total * 0.95 # 5% off
return totalbookstore/inventory.py:
class Inventory:
"""Manage bookstore inventory."""
def __init__(self):
self.books = {} # isbn -> Book object
def add_book(self, book):
"""Add a book to inventory."""
if book.isbn in self.books:
self.books[book.isbn].quantity += book.quantity
else:
self.books[book.isbn] = book
def find_by_isbn(self, isbn):
"""Find book by ISBN."""
return self.books.get(isbn)
def search_by_title(self, title):
"""Search books by title (case-insensitive)."""
title_lower = title.lower()
return [book for book in self.books.values()
if title_lower in book.title.lower()]
def get_all_books(self):
"""Get all books in inventory."""
return list(self.books.values())
def get_in_stock(self):
"""Get only books in stock."""
return [book for book in self.books.values() if book.is_in_stock()]bookstore/utils.py:
def format_currency(amount):
"""Format amount as currency."""
return f"${amount:.2f}"
def print_separator(char='=', length=50):
"""Print a separator line."""
print(char * length)
def display_books(books, title="Books"):
"""Display a list of books."""
print_separator()
print(f" {title}")
print_separator()
if not books:
print(" No books to display.")
else:
for i, book in enumerate(books, 1):
stock = f"(In Stock: {book.quantity})" if book.is_in_stock() else "(Out of Stock)"
print(f" {i}. {book} {stock}")
print_separator()bookstore/__init__.py:
"""
Bookstore Management System
A complete package for managing a bookstore.
"""
from .book import Book
from .inventory import Inventory
from .pricing import calculate_discount, calculate_total, apply_bulk_discount
from .utils import format_currency, display_books
__version__ = "1.0.0"
__all__ = ['Book', 'Inventory', 'calculate_discount', 'calculate_total',
'apply_bulk_discount', 'format_currency', 'display_books']main.py:
from bookstore import Book, Inventory, calculate_discount, format_currency, display_books
def main():
# Create inventory
inventory = Inventory()
# Add books
books_data = [
("9781593279288", "Python Crash Course", "Eric Matthes", 39.99, 15),
("9781491957660", "Fluent Python", "Luciano Ramalho", 49.99, 8),
("9781449355739", "Learning Python", "Mark Lutz", 59.99, 12),
("9781491946008", "Effective Python", "Brett Slatkin", 44.99, 5),
("9780134853987", "Python Cookbook", "David Beazley", 54.99, 0),
]
for isbn, title, author, price, qty in books_data:
book = Book(isbn, title, author, price, qty)
inventory.add_book(book)
# Display all books
display_books(inventory.get_all_books(), "All Books in Inventory")
# Display only in-stock books
print()
display_books(inventory.get_in_stock(), "Books In Stock")
# Search for books
print()
search_term = "Python"
results = inventory.search_by_title(search_term)
display_books(results, f"Search Results for '{search_term}'")
# Calculate discounted price
print()
book = inventory.find_by_isbn("9781593279288")
if book:
original = book.price
discounted = calculate_discount(original, 20)
print(f"\n{book.title}")
print(f" Original price: {format_currency(original)}")
print(f" With 20% off: {format_currency(discounted)}")
if __name__ == "__main__":
main()Output:
==================================================
All Books in Inventory
==================================================
1. Python Crash Course by Eric Matthes ($39.99) (In Stock: 15)
2. Fluent Python by Luciano Ramalho ($49.99) (In Stock: 8)
3. Learning Python by Mark Lutz ($59.99) (In Stock: 12)
4. Effective Python by Brett Slatkin ($44.99) (In Stock: 5)
5. Python Cookbook by David Beazley ($54.99) (Out of Stock)
==================================================
==================================================
Books In Stock
==================================================
1. Python Crash Course by Eric Matthes ($39.99) (In Stock: 15)
2. Fluent Python by Luciano Ramalho ($49.99) (In Stock: 8)
3. Learning Python by Mark Lutz ($59.99) (In Stock: 12)
4. Effective Python by Brett Slatkin ($44.99) (In Stock: 5)
==================================================
==================================================
Search Results for 'Python'
==================================================
1. Python Crash Course by Eric Matthes ($39.99) (In Stock: 15)
2. Fluent Python by Luciano Ramalho ($49.99) (In Stock: 8)
3. Learning Python by Mark Lutz ($59.99) (In Stock: 12)
4. Effective Python by Brett Slatkin ($44.99) (In Stock: 5)
5. Python Cookbook by David Beazley ($54.99) (Out of Stock)
==================================================
Python Crash Course
Original price: $39.99
With 20% off: $31.99Best Practices for Modules and Packages
1. One Module, One Purpose
Each module should have a clear, single purpose:
Good:
bookstore/
validation.py # All validation functions
pricing.py # All pricing calculations
models.py # All data modelsAvoid:
bookstore/
stuff.py # Random unrelated functions
utilities.py # Too vague2. Use Clear, Descriptive Names
# Good
from bookstore.pricing import calculate_discount
from bookstore.validation import validate_isbn
# Avoid
from bookstore.utils import calc_disc # Too abbreviated
from bookstore.helpers import do_thing # Too vague3. Document Your Modules
"""
pricing.py - Pricing and discount calculations
This module provides functions for calculating prices,
discounts, and taxes in the bookstore system.
Functions:
calculate_discount(price, percent) - Calculate discounted price
calculate_tax(price, rate) - Calculate tax amount
format_currency(amount) - Format amount as currency string
"""
def calculate_discount(price, discount_percent):
"""
Calculate price after applying a discount.
Args:
price (float): Original price
discount_percent (float): Discount percentage (0-100)
Returns:
float: Price after discount
Example:
>>> calculate_discount(100, 20)
80.0
"""
discount_amount = price * (discount_percent / 100)
return price - discount_amount4. Use __all__ to Control Exports
# book_utils.py
def format_title(title):
return title.title()
def _internal_helper(data):
# Private function, not exported
pass
# Only export public functions
__all__ = ['format_title']5. Avoid Circular Imports
Bad:
# module_a.py
from module_b import func_b
def func_a():
return func_b()
# module_b.py
from module_a import func_a # Circular import!
def func_b():
return func_a()Solution: Restructure code or use local imports.
6. Keep Module Files Manageable
- If a module exceeds ~500 lines, consider splitting it
- If you have many related functions, create a package
- Use sub-packages for complex hierarchies
Summary
In this lesson, we learned about Python modules and packages:
What We Covered
- Modules: Python files containing reusable code
- Why modules matter: Organization, reusability, maintainability
- Creating modules: Writing
.pyfiles with functions and classes - Importing: Different import styles and when to use each
if __name__ == "__main__": Making modules dual-purpose- Packages: Organizing multiple modules with
__init__.py - Standard library: Built-in modules available in Python
- Third-party modules: Installing external packages with
pip
Key Takeaways
- Modules organize code into logical, reusable units
- Import strategically: Use clear import statements
- Packages scale: Use packages for larger projects
- Standard library first: Check if functionality exists before installing
- Document well: Make your modules easy to understand and use
Module Design Principles
- Single responsibility: One module, one purpose
- Clear naming: Make the purpose obvious
- Good documentation: Explain what and why
- Minimal dependencies: Keep modules independent when possible
- Test separately: Each module should be testable on its own
Modules and packages are fundamental to writing professional Python code. They enable you to build large, maintainable applications and share code effectively across projects. As you work on bigger projects, good module organization becomes essential for success.
In your next Python projects, think about how to break your code into logical modules from the start. It will make development easier and your code more maintainable!