Python Modules: Code Organization
Welcome to Python Modules! Think of modules as filing cabinets for your code - they help you organize functions, classes, and variables into logical, reusable components.
What is a Module?
A module is simply a Python file (.py) that contains code you can import and reuse. Every Python file you create is a module!
# greeting.py - This is a module!
def say_hello(name):
return f"Hello, {name}!"
def say_goodbye(name):
return f"Goodbye, {name}!"
PI = 3.14159
AUTHOR = "Your Name"
Creating Your First Module
Letβs create a simple module and use it:
# math_operations.py
def add(a, b):
"""Add two numbers together."""
return a + b
def subtract(a, b):
"""Subtract b from a."""
return a - b
def multiply(a, b):
"""Multiply two numbers."""
return a * b
def divide(a, b):
"""Divide a by b."""
if b == 0:
raise ValueError("Cannot divide by zero!")
return a / b
# Module-level variables
PI = 3.14159
E = 2.71828
Now letβs use this module:
# main.py
import math_operations
# Use functions from the module
result1 = math_operations.add(5, 3)
result2 = math_operations.multiply(4, 7)
print(f"5 + 3 = {result1}")
print(f"4 Γ 7 = {result2}")
print(f"Ο β {math_operations.PI}")
# This will cause an error
try:
math_operations.divide(10, 0)
except ValueError as e:
print(f"Error: {e}")
Import Methods
Python offers several ways to import modules:
1. Import the Whole Module
import math_operations
result = math_operations.add(1, 2)
Pros: Clear where functions come from, avoids naming conflicts Cons: Longer function calls
2. Import Specific Items
from math_operations import add, multiply, PI
result = add(1, 2) # No module prefix needed
area = multiply(PI, 5**2) # Direct access
Pros: Shorter code, cleaner Cons: Can cause naming conflicts, unclear origins
3. Import with Alias
import math_operations as mo
result = mo.add(1, 2)
Pros: Short prefix, clear module origin Cons: Extra typing for the alias
4. Import Everything (Avoid This!)
from math_operations import *
result = add(1, 2) # Works, but dangerous!
Cons: Pollutes namespace, unclear whatβs imported, hard to debug
The if __name__ == "__main__" Pattern
A special pattern for making modules both importable and runnable:
# calculator.py
def add(a, b):
return a + b
def main():
"""Main function when run as script."""
print("Simple Calculator")
print(f"2 + 3 = {add(2, 3)}")
if __name__ == "__main__":
main()
What happens:
- When you run
python calculator.py:__name__is"__main__", somain()runs - When you
import calculator:__name__is"calculator", somain()doesnβt run
Module Search Path
When you import a module, Python searches in this order:
- Built-in modules (
sys,os,math) - Current directory (where your script runs)
- PYTHONPATH environment variable
- Installation directories (site-packages)
import sys
print(sys.path) # Shows the search path
Module Execution
When a module is imported, Python:
- Finds the module file
- Compiles it to bytecode (
.pycfiles) - Executes all top-level code
- Creates a module object
- Adds it to
sys.modules
# demo_execution.py
print("This runs when the module is imported!")
def test_function():
print("This only runs when called")
print("Module loaded successfully")
Module Attributes
Every module has special attributes:
import math_operations
print(f"Module name: {math_operations.__name__}")
print(f"Module file: {math_operations.__file__}")
print(f"Module doc: {math_operations.__doc__}")
# List all attributes
print(dir(math_operations))
Practical Examples
Example 1: Configuration Module
# config.py
# Application configuration
DATABASE_URL = "sqlite:///app.db"
DEBUG = True
SECRET_KEY = "your-secret-key-here"
# API settings
API_BASE_URL = "https://api.example.com"
TIMEOUT = 30
# Email settings
SMTP_SERVER = "smtp.gmail.com"
SMTP_PORT = 587
# app.py
import config
def send_email(to, subject, body):
# Use config values
print(f"Connecting to {config.SMTP_SERVER}:{config.SMTP_PORT}")
print(f"Sending email to {to}")
if config.DEBUG:
print("Running in debug mode")
send_email("user@example.com", "Hello", "Test message")
Example 2: Utility Functions
# string_utils.py
def capitalize_words(text):
"""Capitalize the first letter of each word."""
return text.title()
def slugify(text):
"""Convert text to URL-friendly slug."""
import re
text = text.lower()
text = re.sub(r'[^a-z0-9]+', '-', text)
return text.strip('-')
def truncate(text, length=100, suffix="..."):
"""Truncate text to specified length."""
if len(text) <= length:
return text
return text[:length - len(suffix)] + suffix
# blog_post.py
from string_utils import slugify, truncate
class BlogPost:
def __init__(self, title, content):
self.title = title
self.slug = slugify(title)
self.excerpt = truncate(content, 150)
def get_url(self):
return f"/blog/{self.slug}"
# Test the blog post
post = BlogPost("Hello World!", "This is a long blog post content...")
print(f"Title: {post.title}")
print(f"Slug: {post.slug}")
print(f"URL: {post.get_url()}")
print(f"Excerpt: {post.excerpt}")
Example 3: Data Validation
# validators.py
def is_valid_email(email):
"""Check if email is valid."""
import re
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, email))
def is_valid_phone(phone):
"""Check if phone number is valid (US format)."""
import re
pattern = r'^\+?1?[-.\s]?\(?([0-9]{3})\)?[-.\s]?([0-9]{3})[-.\s]?([0-9]{4})$'
return bool(re.match(pattern, phone))
def is_strong_password(password):
"""Check if password meets strength requirements."""
if len(password) < 8:
return False
if not any(c.isupper() for c in password):
return False
if not any(c.islower() for c in password):
return False
if not any(c.isdigit() for c in password):
return False
return True
# user_registration.py
from validators import is_valid_email, is_strong_password
class User:
def __init__(self, email, password):
if not is_valid_email(email):
raise ValueError("Invalid email address")
if not is_strong_password(password):
raise ValueError("Password is not strong enough")
self.email = email
self.password = password # In real app, hash this!
def __str__(self):
return f"User(email='{self.email}')"
# Test user registration
try:
user1 = User("alice@example.com", "StrongPass123!")
print(f"Created: {user1}")
user2 = User("invalid-email", "weak")
print(f"Created: {user2}")
except ValueError as e:
print(f"Registration failed: {e}")
Common Module Patterns
Constants Module
# constants.py
# Application constants
MAX_FILE_SIZE = 10 * 1024 * 1024 # 10MB
DEFAULT_TIMEOUT = 30
SUPPORTED_FORMATS = ['jpg', 'png', 'gif', 'webp']
# API endpoints
API_BASE = "https://api.example.com"
ENDPOINTS = {
'users': f"{API_BASE}/users",
'posts': f"{API_BASE}/posts",
'comments': f"{API_BASE}/comments"
}
Logger Module
# logger.py
import logging
import sys
def setup_logger(name, level=logging.INFO):
"""Setup a logger with console output."""
logger = logging.getLogger(name)
logger.setLevel(level)
# Create console handler
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(level)
# Create formatter
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
# Add handler to logger
logger.addHandler(handler)
return logger
# Create default logger
default_logger = setup_logger(__name__)
Database Module
# database.py
import sqlite3
from contextlib import contextmanager
class Database:
def __init__(self, db_path):
self.db_path = db_path
@contextmanager
def get_connection(self):
"""Context manager for database connections."""
conn = sqlite3.connect(self.db_path)
try:
yield conn
finally:
conn.close()
def execute_query(self, query, params=None):
"""Execute a query and return results."""
with self.get_connection() as conn:
cursor = conn.cursor()
if params:
cursor.execute(query, params)
else:
cursor.execute(query)
return cursor.fetchall()
# Create database instance
db = Database("app.db")
Best Practices
1. Use Descriptive Names
# Good
import user_management
from data_processing import clean_data, validate_input
# Bad
import um
from dp import cd, vi
2. Organize Related Functions
# Good: Group related functions in modules
# string_utils.py - String operations
# file_utils.py - File operations
# math_utils.py - Math operations
# Bad: One huge utils.py with everything
3. Use __all__ for Explicit Exports
# my_module.py
__all__ = ['public_function', 'PublicClass'] # Only these are public
def public_function():
pass
def _private_function(): # Not in __all__, considered private
pass
class PublicClass:
pass
class _PrivateClass: # Not in __all__, considered private
pass
4. Document Your Modules
"""
String Utilities Module
This module provides utility functions for string manipulation,
including formatting, validation, and transformation operations.
Author: Your Name
Version: 1.0.0
"""
def format_name(first, last):
"""Format a full name from first and last names."""
return f"{first} {last}".title()
Practice Exercises
Exercise 1: Temperature Converter
Create a temperature.py module with functions to convert between Celsius, Fahrenheit, and Kelvin. Include:
celsius_to_fahrenheit()fahrenheit_to_celsius()celsius_to_kelvin()kelvin_to_celsius()- Constants for freezing/boiling points
Exercise 2: File Organizer
Create a file_organizer.py module with functions to:
get_file_extension(filename)get_file_size(filepath)organize_by_extension(directory)organize_by_date(directory)
Exercise 3: Text Statistics
Create a text_stats.py module with functions to:
count_words(text)count_sentences(text)average_word_length(text)most_common_words(text, n=10)
Exercise 4: Currency Converter
Create a currency.py module with:
- Exchange rate constants
convert_currency(amount, from_currency, to_currency)format_currency(amount, currency_code)- Support for USD, EUR, GBP, JPY
Summary
Modules are Pythonβs way of organizing code:
Creating Modules:
# my_module.py
def my_function():
return "Hello from module!"
CONSTANT = 42
Importing Modules:
import my_module # Full import
from my_module import my_function # Specific import
import my_module as mm # With alias
Key Concepts:
- Every
.pyfile is a module - Use
if __name__ == "__main__"for runnable scripts - Python searches
sys.pathfor modules - Use
__all__to control whatβs exported
Best Practices:
- Use descriptive module names
- Group related functions together
- Document your modules with docstrings
- Use explicit imports over
from module import *
Next: Packages - organizing modules into directories! π