Python Modules
Organize and reuse code with Python's powerful module system
📦 What are Modules?
Modules are Python files containing definitions, statements, and functions that can be imported and used in other Python programs. They help organize code, promote reusability, and create maintainable applications.
# Example of importing and using modules
import math # Import math module
import random # Import random module
import datetime # Import datetime module
# Using functions from modules
pi = math.pi # Access PI constant
rand_num = random.randint(1, 10) # Generate random number
current_time = datetime.datetime.now() # Get current time
Module System Theory
Understanding how Python's module system works is essential for building scalable applications:
🧠 Core Concepts
Module Search Path
Python searches for modules in specific directories defined in sys.path
Import Mechanism
Python compiles and caches modules for efficient reuse
Namespace Isolation
Each module has its own namespace, preventing naming conflicts
Circular Imports
Understanding and avoiding circular import dependencies
🎯 Module Benefits
- Code Reusability: Write once, use everywhere
- Organization: Logical grouping of related functionality
- Namespace Management: Avoid naming conflicts
- Maintainability: Easier to update and debug
- Collaboration: Team members can work on separate modules
Creating Your First Module
Let's start by creating a simple module and learning different ways to import it:
# math_utils.py - A simple math utilities module
"""
Math Utilities Module
Provides basic mathematical functions and constants.
"""
# Module-level constants
PI = 3.14159
E = 2.71828
# Simple functions
def add(a, b):
"""Add two numbers."""
return a + b
def multiply(a, b):
"""Multiply two numbers."""
return a * b
def circle_area(radius):
"""Calculate the area of a circle."""
return PI * radius ** 2
def factorial(n):
"""Calculate factorial of a number."""
if n <= 1:
return 1
return n * factorial(n - 1)
# Module initialization code (runs when imported)
print(f"Math utilities module loaded! PI = {PI}")
# This runs only when the module is executed directly
if __name__ == "__main__":
print("Testing math_utils module:")
print(f"5 + 3 = {add(5, 3)}")
print(f"4 * 6 = {multiply(4, 6)}")
print(f"Circle area (r=5) = {circle_area(5)}")
print(f"Factorial of 5 = {factorial(5)}")
# main.py - Using our math_utils module
# Method 1: Import entire module
import math_utils
result1 = math_utils.add(10, 5)
area = math_utils.circle_area(3)
print(f"Addition: {result1}")
print(f"Circle area: {area}")
print(f"PI constant: {math_utils.PI}")
# Method 2: Import specific functions
from math_utils import multiply, factorial
result2 = multiply(4, 7)
fact = factorial(4)
print(f"Multiplication: {result2}")
print(f"Factorial: {fact}")
# Method 3: Import with alias
import math_utils as math
result3 = math.add(20, 30)
print(f"Using alias: {result3}")
# Method 4: Import specific items with alias
from math_utils import circle_area as calc_area
area2 = calc_area(10)
print(f"Area with alias: {area2}")
# Method 5: Import all (use with caution!)
# from math_utils import *
# This imports all public names from the module
📥 Import Methods Summary
Imports entire module, access with module.name
Imports specific items directly
Imports with a custom name
Imports all public names (use carefully)
Essential Built-in Modules
Python comes with a rich standard library. Let's explore some essential built-in modules:
# os module - Operating system interface
import os
print(f"Current directory: {os.getcwd()}")
print(f"User home: {os.path.expanduser('~')}")
print(f"Path separator: {os.sep}")
# sys module - System-specific parameters
import sys
print(f"Python version: {sys.version}")
print(f"Platform: {sys.platform}")
print(f"Module search path: {sys.path[:3]}...") # First 3 paths
# datetime module - Date and time handling
from datetime import datetime, date, timedelta
now = datetime.now()
today = date.today()
tomorrow = today + timedelta(days=1)
print(f"Current time: {now}")
print(f"Today: {today}")
print(f"Tomorrow: {tomorrow}")
# random module - Generate random numbers
import random
print(f"Random integer (1-10): {random.randint(1, 10)}")
print(f"Random float (0-1): {random.random()}")
print(f"Random choice: {random.choice(['apple', 'banana', 'orange'])}")
# math module - Mathematical functions
import math
print(f"Square root of 16: {math.sqrt(16)}")
print(f"Sine of 90 degrees: {math.sin(math.radians(90))}")
print(f"Pi constant: {math.pi}")
# json module - JSON encoder/decoder
import json
data = {'name': 'Alice', 'age': 30, 'city': 'New York'}
json_string = json.dumps(data)
parsed_data = json.loads(json_string)
print(f"JSON string: {json_string}")
print(f"Parsed data: {parsed_data}")
# re module - Regular expressions
import re
text = "Contact us at [email protected] or [email protected]"
emails = re.findall(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b', text)
print(f"Found emails: {emails}")
📚 Standard Library Categories
File & OS
os, sys, pathlib, shutil, glob
Data Processing
json, csv, xml, pickle, sqlite3
Math & Science
math, random, statistics, decimal
Text & Patterns
re, string, textwrap, difflib
Date & Time
datetime, time, calendar
Internet & Web
urllib, http, email, html
Creating Packages
Packages are directories containing multiple modules. They help organize large projects:
📁 Package Structure Example
my_package/
__init__.py # Makes it a package
math_operations/
__init__.py
basic.py # Basic math functions
advanced.py # Advanced math functions
string_utils/
__init__.py
formatters.py # String formatting
validators.py # String validation
tests/
__init__.py
test_math.py
test_strings.py
# my_package/__init__.py
"""
My Package - A collection of utility modules
"""
__version__ = "1.0.0"
__author__ = "Your Name"
# Import commonly used functions to package level
from .math_operations.basic import add, subtract
from .string_utils.formatters import title_case
# Package-level constants
DEFAULT_ENCODING = "utf-8"
print(f"My Package v{__version__} loaded")
# my_package/math_operations/basic.py
"""Basic mathematical operations."""
def add(a, b):
"""Add two numbers."""
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
# my_package/math_operations/advanced.py
"""Advanced mathematical operations."""
import math
def power(base, exponent):
"""Calculate base raised to exponent."""
return base ** exponent
def square_root(number):
"""Calculate square root."""
if number < 0:
raise ValueError("Cannot calculate square root of negative number")
return math.sqrt(number)
def logarithm(number, base=math.e):
"""Calculate logarithm."""
if number <= 0:
raise ValueError("Number must be positive")
if base == math.e:
return math.log(number)
return math.log(number, base)
# my_package/string_utils/formatters.py
"""String formatting utilities."""
def title_case(text):
"""Convert text to title case."""
return text.title()
def snake_case(text):
"""Convert text to snake_case."""
return text.lower().replace(' ', '_')
def camel_case(text):
"""Convert text to camelCase."""
words = text.split()
return words[0].lower() + ''.join(word.capitalize() for word in words[1:])
def format_phone(phone):
"""Format phone number."""
digits = ''.join(filter(str.isdigit, phone))
if len(digits) == 10:
return f"({digits[:3]}) {digits[3:6]}-{digits[6:]}"
return phone
# Using the package
import my_package
# Use functions imported at package level
result = my_package.add(10, 5)
formatted = my_package.title_case("hello world")
print(f"Addition: {result}")
print(f"Title case: {formatted}")
# Import specific modules
from my_package.math_operations import advanced
from my_package.string_utils import formatters
# Use advanced math functions
sqrt_result = advanced.square_root(25)
log_result = advanced.logarithm(100, 10)
print(f"Square root: {sqrt_result}")
print(f"Log base 10: {log_result}")
# Use string formatters
snake = formatters.snake_case("Hello World")
camel = formatters.camel_case("hello world")
phone = formatters.format_phone("1234567890")
print(f"Snake case: {snake}")
print(f"Camel case: {camel}")
print(f"Formatted phone: {phone}")
# Import with aliases
from my_package.math_operations.basic import multiply as mult
from my_package.string_utils.formatters import format_phone as fmt_phone
result = mult(6, 7)
formatted_phone = fmt_phone("555-123-4567")
print(f"Multiplication: {result}")
print(f"Phone: {formatted_phone}")
Module Search Path
Understanding how Python finds modules is crucial for debugging import issues:
import sys
import os
# Display current module search path
print("Python Module Search Path:")
for i, path in enumerate(sys.path):
print(f"{i+1}. {path}")
# Add a custom directory to the search path
custom_path = "/path/to/my/modules"
if custom_path not in sys.path:
sys.path.append(custom_path)
print(f"\nAdded custom path: {custom_path}")
# Check if a module can be found
def find_module(module_name):
"""Check where a module would be loaded from."""
import importlib.util
spec = importlib.util.find_spec(module_name)
if spec is None:
print(f"Module '{module_name}' not found")
else:
print(f"Module '{module_name}' found at: {spec.origin}")
# Test with some modules
find_module("os")
find_module("json")
find_module("my_custom_module") # This might not exist
# Show information about loaded modules
print(f"\nCurrently loaded modules: {len(sys.modules)}")
print("Some loaded modules:")
for i, module_name in enumerate(list(sys.modules.keys())[:10]):
print(f" {module_name}")
# Environment variables affecting module search
print(f"\nPYTHONPATH: {os.environ.get('PYTHONPATH', 'Not set')}")
# Working directory (always first in search path)
print(f"Current working directory: {os.getcwd()}")
# Demonstrate relative imports (in a package context)
# This would be in a package file:
"""
# Relative imports (use in packages)
from . import sibling_module # Same package
from .subpackage import module # Subpackage
from ..parent_package import module # Parent package
"""
🔍 Module Search Order
- Current Directory: Where the script is running
- PYTHONPATH: Environment variable directories
- Standard Library: Built-in Python modules
- Site-packages: Third-party installed packages
- sys.path: Programmatically added paths
Module Best Practices
Naming Conventions
- Use lowercase with underscores for module names
- Keep names short but descriptive
- Avoid names that conflict with built-ins
- Use __init__.py for package initialization
Structure & Organization
- Group related functionality in packages
- Keep modules focused and cohesive
- Use clear directory structure
- Document module purpose and usage
Import Guidelines
- Import at the top of files
- Group imports: standard, third-party, local
- Use specific imports when possible
- Avoid wildcard imports (*)
Security & Performance
- Be careful with dynamic imports
- Use __all__ to control public interface
- Avoid circular imports
- Consider lazy imports for heavy modules
# Good import organization
# Standard library imports
import os
import sys
from datetime import datetime, timedelta
# Third-party imports
import requests
import numpy as np
# Local application imports
from my_package import config
from my_package.utils import helpers
from .models import User
# Module with proper structure
"""
user_manager.py - User management utilities
This module provides functions for managing user accounts,
including creation, validation, and authentication.
"""
# Module constants
DEFAULT_ROLE = "user"
MAX_LOGIN_ATTEMPTS = 3
# Control public interface
__all__ = ['create_user', 'validate_user', 'authenticate']
# Private helper function (not in __all__)
def _hash_password(password):
"""Hash password using secure algorithm."""
import hashlib
return hashlib.sha256(password.encode()).hexdigest()
# Public functions
def create_user(username, email, password, role=DEFAULT_ROLE):
"""
Create a new user account.
Args:
username (str): Unique username
email (str): User's email address
password (str): Plain text password
role (str): User role, defaults to 'user'
Returns:
dict: User information
Raises:
ValueError: If username or email is invalid
"""
if not username or not email:
raise ValueError("Username and email are required")
return {
'username': username,
'email': email,
'password_hash': _hash_password(password),
'role': role,
'created_at': datetime.now()
}
def validate_user(user_data):
"""Validate user data structure."""
required_fields = ['username', 'email', 'password_hash']
return all(field in user_data for field in required_fields)
def authenticate(username, password, users_db):
"""Authenticate user credentials."""
user = users_db.get(username)
if user and user['password_hash'] == _hash_password(password):
return True
return False
# Module initialization
if __name__ == "__main__":
# Test the module
test_user = create_user("alice", "[email protected]", "secret123")
print(f"Created user: {test_user['username']}")
print(f"Valid user data: {validate_user(test_user)}")
🧠 Test Your Knowledge
Test your understanding of Python modules: