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
                                    
import
Statement
Reusable
Code
Organized
Structure

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:

Creating a Module (math_utils.py)
# 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)}")
Using the Module (main.py)
# 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

import module

Imports entire module, access with module.name

from module import name

Imports specific items directly

import module as alias

Imports with a custom name

from module import *

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:

Common 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
Package Files
# 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
# 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:

Module Search Path
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

  1. Current Directory: Where the script is running
  2. PYTHONPATH: Environment variable directories
  3. Standard Library: Built-in Python modules
  4. Site-packages: Third-party installed packages
  5. 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
Best Practices Example
# 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:

Question 1: What file makes a directory a Python package?

Question 2: Which import statement imports specific functions directly?

Question 3: What does the __all__ variable control in a module?