Modules and packages are essential for organizing code into reusable components. This lesson covers creating your own modules, working with Python's standard library, and managing dependencies with virtual environments.
Understanding Modules
What are Modules?
A module is a file containing Python definitions and statements. The file name is the module name with the suffix .py added.
# Example: math_utils.py (this would be a module)
def add(a, b):
"""Add two numbers."""
return a + b
def multiply(a, b):
"""Multiply two numbers."""
return a * b
def power(base, exponent):
"""Calculate base raised to the power of exponent."""
return base ** exponent
# Constants
PI = 3.14159
E = 2.71828
Importing Modules
# Different ways to import modules
import math # Import entire module
from math import sqrt, pi # Import specific functions/constants
from math import * # Import everything (not recommended)
import math as m # Import with alias
# Using imported modules
print(math.sqrt(16)) # Using full module name
print(sqrt(16)) # Using imported function directly
print(pi) # Using imported constant
print(m.sqrt(25)) # Using alias
# Import your own module
import math_utils
result = math_utils.add(5, 3)
print(f"Result: {result}")
Creating Your Own Modules
Simple Module Example
# File: calculator.py
"""
A simple calculator module with basic 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:
return a / b
else:
raise ValueError("Cannot divide by zero")
def power(base, exponent):
"""Calculate base raised to the power of exponent."""
return base ** exponent
# Constants
OPERATORS = ['+', '-', '*', '/', '**']
# Module-level code (runs when imported)
print("Calculator module loaded successfully")
Using Your Module
# File: main.py
import calculator
# Use functions from the module
result1 = calculator.add(10, 5)
result2 = calculator.multiply(4, 3)
result3 = calculator.power(2, 8)
print(f"Addition: {result1}")
print(f"Multiplication: {result2}")
print(f"Power: {result3}")
# Access constants
print(f"Available operators: {calculator.OPERATORS}")
# Import specific functions
from calculator import add, multiply
result4 = add(20, 30)
result5 = multiply(6, 7)
print(f"Direct import - Add: {result4}")
print(f"Direct import - Multiply: {result5}")
Module Attributes and Special Variables
Understanding __name__
# File: module_demo.py
def greet(name):
"""Greet someone."""
return f"Hello, {name}!"
def goodbye(name):
"""Say goodbye to someone."""
return f"Goodbye, {name}!"
# This code runs when the module is imported
print("Module is being imported")
# This code only runs when the module is run directly
if __name__ == "__main__":
print("This module is being run directly")
print(greet("World"))
print(goodbye("World"))
else:
print("This module is being imported by another module")
Module Documentation and Metadata
# File: student_manager.py
"""
Student Management Module
This module provides functionality to manage student records,
including adding, updating, and querying student information.
"""
__version__ = "1.0.0"
__author__ = "Python Instructor"
__email__ = "instructor@example.com"
class Student:
"""A simple Student class."""
def __init__(self, name, student_id, grade):
self.name = name
self.student_id = student_id
self.grade = grade
def __str__(self):
return f"Student({self.name}, ID: {self.student_id}, Grade: {self.grade})"
def create_student(name, student_id, grade):
"""Create a new student."""
return Student(name, student_id, grade)
def get_student_info(student):
"""Get formatted student information."""
return f"Name: {student.name}, ID: {student.student_id}, Grade: {student.grade}"
# Module-level variables
DEFAULT_GRADE = "A"
MAX_STUDENTS = 100
if __name__ == "__main__":
# Test the module
student = create_student("Alice", "S001", "A")
print(get_student_info(student))
Working with Python's Standard Library
Common Standard Library Modules
# File: standard_library_demo.py
import os
import sys
import datetime
import random
import json
import csv
import math
import statistics
from collections import Counter, defaultdict
import urllib.request
import re
def demonstrate_os_module():
"""Demonstrate the os module."""
print("=== OS Module ===")
print(f"Current working directory: {os.getcwd()}")
print(f"Platform: {os.name}")
print(f"Environment variables: {len(os.environ)}")
# List files in current directory
files = os.listdir('.')
print(f"Files in current directory: {len(files)}")
def demonstrate_datetime_module():
"""Demonstrate the datetime module."""
print("\n=== Datetime Module ===")
now = datetime.datetime.now()
print(f"Current date and time: {now}")
print(f"Year: {now.year}, Month: {now.month}, Day: {now.day}")
# Format dates
formatted = now.strftime("%Y-%m-%d %H:%M:%S")
print(f"Formatted: {formatted}")
# Calculate time differences
tomorrow = now + datetime.timedelta(days=1)
print(f"Tomorrow: {tomorrow}")
def demonstrate_random_module():
"""Demonstrate the random module."""
print("\n=== Random Module ===")
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'])}")
# Shuffle a list
numbers = [1, 2, 3, 4, 5]
random.shuffle(numbers)
print(f"Shuffled numbers: {numbers}")
def demonstrate_collections_module():
"""Demonstrate the collections module."""
print("\n=== Collections Module ===")
# Counter
text = "hello world"
counter = Counter(text)
print(f"Letter counts: {counter}")
print(f"Most common: {counter.most_common(3)}")
# DefaultDict
dd = defaultdict(list)
dd['fruits'].append('apple')
dd['fruits'].append('banana')
dd['vegetables'].append('carrot')
print(f"DefaultDict: {dict(dd)}")
def demonstrate_regex_module():
"""Demonstrate the re (regex) module."""
print("\n=== Regex Module ===")
text = "Contact us at support@example.com or call 555-123-4567"
# Find email
email_pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
emails = re.findall(email_pattern, text)
print(f"Emails found: {emails}")
# Find phone numbers
phone_pattern = r'\b\d{3}-\d{3}-\d{4}\b'
phones = re.findall(phone_pattern, text)
print(f"Phone numbers found: {phones}")
# Run demonstrations
demonstrate_os_module()
demonstrate_datetime_module()
demonstrate_random_module()
demonstrate_collections_module()
demonstrate_regex_module()
Packages
Creating a Package
A package is a collection of modules organized in directories.
my_package/
├── __init__.py
├── math_utils.py
├── string_utils.py
└── data_utils.py
Package Structure Example
# File: my_package/__init__.py
"""
My Package - A collection of utility modules.
This package provides various utility functions for common programming tasks.
"""
__version__ = "1.0.0"
__author__ = "Python Instructor"
# Import key functions to make them available at package level
from .math_utils import add, multiply, power
from .string_utils import format_name, count_words
from .data_utils import process_list, save_data
# Package-level variables
PACKAGE_NAME = "My Utility Package"
SUPPORTED_FORMATS = ['txt', 'csv', 'json']
# This runs when the package is imported
print(f"{PACKAGE_NAME} v{__version__} loaded successfully")
# File: my_package/math_utils.py
def add(a, b):
"""Add two numbers."""
return a + b
def multiply(a, b):
"""Multiply two numbers."""
return a * b
def power(base, exponent):
"""Calculate power."""
return base ** exponent
def factorial(n):
"""Calculate factorial."""
if n <= 1:
return 1
return n * factorial(n - 1)
# File: my_package/string_utils.py
def format_name(first, last):
"""Format a full name."""
return f"{first.title()} {last.title()}"
def count_words(text):
"""Count words in text."""
return len(text.split())
def reverse_string(text):
"""Reverse a string."""
return text[::-1]
# File: my_package/data_utils.py
import json
import csv
def process_list(data, operation="sum"):
"""Process a list with various operations."""
if operation == "sum":
return sum(data)
elif operation == "average":
return sum(data) / len(data)
elif operation == "max":
return max(data)
elif operation == "min":
return min(data)
else:
return data
def save_data(data, filename, format="json"):
"""Save data to file in specified format."""
if format == "json":
with open(filename, 'w') as f:
json.dump(data, f, indent=2)
elif format == "csv":
with open(filename, 'w', newline='') as f:
writer = csv.writer(f)
if isinstance(data, list) and len(data) > 0:
if isinstance(data[0], dict):
writer.writerow(data[0].keys())
for row in data:
writer.writerow(row.values())
else:
writer.writerows(data)
Using Your Package
# File: package_demo.py
# Import the entire package
import my_package
# Use functions imported in __init__.py
result1 = my_package.add(5, 3)
result2 = my_package.format_name("john", "doe")
print(f"Add: {result1}")
print(f"Formatted name: {result2}")
# Import specific modules
from my_package import math_utils, string_utils
result3 = math_utils.factorial(5)
result4 = string_utils.reverse_string("Python")
print(f"Factorial: {result3}")
print(f"Reversed: {result4}")
# Import specific functions
from my_package.math_utils import factorial
from my_package.string_utils import count_words
result5 = factorial(6)
result6 = count_words("Hello world from Python")
print(f"Factorial 6: {result5}")
print(f"Word count: {result6}")
# Access package-level variables
print(f"Package: {my_package.PACKAGE_NAME}")
print(f"Supported formats: {my_package.SUPPORTED_FORMATS}")
Virtual Environments
Why Use Virtual Environments?
Virtual environments allow you to: - Isolate project dependencies - Use different Python versions - Avoid conflicts between packages - Reproduce exact development environments
Creating and Using Virtual Environments
# File: venv_demo.py
"""
Demonstration of virtual environment concepts and usage.
Note: This is educational content - actual venv commands are run in terminal.
"""
def explain_virtual_environments():
"""Explain virtual environment concepts."""
print("=== Virtual Environments ===")
print("""
Virtual environments are isolated Python environments that allow you to:
1. Install packages without affecting system Python
2. Use different package versions for different projects
3. Avoid dependency conflicts
4. Reproduce exact development environments
Common commands:
- python -m venv myenv # Create virtual environment
- source myenv/bin/activate # Activate (Linux/Mac)
- myenv\\Scripts\\activate # Activate (Windows)
- deactivate # Deactivate
- pip freeze > requirements.txt # Save dependencies
- pip install -r requirements.txt # Install dependencies
""")
def demonstrate_requirements():
"""Show how to manage project dependencies."""
print("\n=== Requirements Management ===")
# Example requirements.txt content
requirements = """
# Core dependencies
requests>=2.25.0
pandas>=1.3.0
numpy>=1.21.0
# Development dependencies
pytest>=6.0.0
black>=21.0.0
flake8>=3.9.0
# Optional dependencies
matplotlib>=3.4.0
jupyter>=1.0.0
"""
print("Example requirements.txt:")
print(requirements)
print("""
To use requirements.txt:
1. Create virtual environment: python -m venv venv
2. Activate environment: source venv/bin/activate (or venv\\Scripts\\activate on Windows)
3. Install dependencies: pip install -r requirements.txt
4. Save current environment: pip freeze > requirements.txt
""")
# Run the demonstration
explain_virtual_environments()
demonstrate_requirements()
Practical Examples
Example 1: Text Processing Package
# File: text_processor/__init__.py
"""
Text Processing Package
A comprehensive package for text manipulation and analysis.
"""
from .analyzer import TextAnalyzer
from .formatter import TextFormatter
from .validator import TextValidator
__version__ = "1.0.0"
# File: text_processor/analyzer.py
import re
from collections import Counter
class TextAnalyzer:
"""Analyze text for various statistics."""
def __init__(self, text):
self.text = text
def word_count(self):
"""Count total words."""
return len(self.text.split())
def character_count(self, include_spaces=True):
"""Count characters."""
if include_spaces:
return len(self.text)
return len(self.text.replace(" ", ""))
def sentence_count(self):
"""Count sentences."""
sentences = re.split(r'[.!?]+', self.text)
return len([s for s in sentences if s.strip()])
def most_common_words(self, n=5):
"""Find most common words."""
words = re.findall(r'\b\w+\b', self.text.lower())
return Counter(words).most_common(n)
def average_word_length(self):
"""Calculate average word length."""
words = self.text.split()
if not words:
return 0
return sum(len(word) for word in words) / len(words)
# File: text_processor/formatter.py
class TextFormatter:
"""Format text in various ways."""
@staticmethod
def title_case(text):
"""Convert to title case."""
return text.title()
@staticmethod
def reverse_text(text):
"""Reverse the text."""
return text[::-1]
@staticmethod
def remove_punctuation(text):
"""Remove punctuation from text."""
import string
return text.translate(str.maketrans('', '', string.punctuation))
@staticmethod
def add_line_numbers(text):
"""Add line numbers to text."""
lines = text.split('\n')
return '\n'.join(f"{i+1:3d}: {line}" for i, line in enumerate(lines))
# File: text_processor/validator.py
import re
class TextValidator:
"""Validate text for various criteria."""
@staticmethod
def is_email(text):
"""Check if text is a valid email."""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return bool(re.match(pattern, text))
@staticmethod
def is_phone(text):
"""Check if text is a valid phone number."""
pattern = r'^\+?1?[-.\s]?\(?[0-9]{3}\)?[-.\s]?[0-9]{3}[-.\s]?[0-9]{4}$'
return bool(re.match(pattern, text))
@staticmethod
def is_palindrome(text):
"""Check if text is a palindrome."""
clean_text = re.sub(r'[^a-zA-Z0-9]', '', text.lower())
return clean_text == clean_text[::-1]
@staticmethod
def has_minimum_length(text, min_length=8):
"""Check if text meets minimum length requirement."""
return len(text) >= min_length
Example 2: Using the Text Processor Package
# File: text_demo.py
from text_processor import TextAnalyzer, TextFormatter, TextValidator
def demonstrate_text_processing():
"""Demonstrate the text processing package."""
sample_text = """
Python is a powerful programming language. It's widely used in data science,
web development, and automation. Python's syntax is clean and readable,
making it great for beginners and experts alike.
Contact us at: support@python.org or call (555) 123-4567
"""
print("=== Text Processing Demo ===")
# Text Analysis
analyzer = TextAnalyzer(sample_text)
print(f"Word count: {analyzer.word_count()}")
print(f"Character count: {analyzer.character_count()}")
print(f"Sentence count: {analyzer.sentence_count()}")
print(f"Average word length: {analyzer.average_word_length():.2f}")
print(f"Most common words: {analyzer.most_common_words(3)}")
# Text Formatting
print(f"\nTitle case: {TextFormatter.title_case('hello world')}")
print(f"Reversed: {TextFormatter.reverse_text('Python')}")
clean_text = TextFormatter.remove_punctuation("Hello, World!")
print(f"Clean text: {clean_text}")
numbered = TextFormatter.add_line_numbers("Line 1\nLine 2\nLine 3")
print(f"Numbered lines:\n{numbered}")
# Text Validation
print(f"\nEmail validation: {TextValidator.is_email('support@python.org')}")
print(f"Phone validation: {TextValidator.is_phone('(555) 123-4567')}")
print(f"Palindrome check: {TextValidator.is_palindrome('racecar')}")
print(f"Minimum length: {TextValidator.has_minimum_length('password123', 8)}")
# Run the demonstration
demonstrate_text_processing()
Best Practices
Module Organization
# File: best_practices_demo.py
"""
Demonstrate best practices for module organization.
"""
# 1. Use clear, descriptive module names
# Good: user_authentication.py, data_validation.py
# Bad: utils.py, helper.py, stuff.py
# 2. Include comprehensive docstrings
def process_user_data(user_data):
"""
Process and validate user data.
Args:
user_data (dict): Dictionary containing user information
Returns:
dict: Processed and validated user data
Raises:
ValueError: If required fields are missing
TypeError: If user_data is not a dictionary
"""
if not isinstance(user_data, dict):
raise TypeError("user_data must be a dictionary")
required_fields = ['name', 'email', 'age']
for field in required_fields:
if field not in user_data:
raise ValueError(f"Missing required field: {field}")
# Process the data
processed_data = user_data.copy()
processed_data['name'] = processed_data['name'].title()
processed_data['email'] = processed_data['email'].lower()
return processed_data
# 3. Use constants for configuration
DEFAULT_TIMEOUT = 30
MAX_RETRIES = 3
SUPPORTED_FORMATS = ['json', 'csv', 'xml']
# 4. Organize imports properly
import os
import sys
import json
from datetime import datetime
from typing import Dict, List, Optional
# Local imports
from .utils import validate_email, format_phone
from .exceptions import ValidationError, ProcessingError
# 5. Use type hints for better code clarity
def calculate_statistics(data: List[float]) -> Dict[str, float]:
"""Calculate basic statistics for a list of numbers."""
if not data:
return {}
return {
'mean': sum(data) / len(data),
'min': min(data),
'max': max(data),
'count': len(data)
}
# 6. Handle errors gracefully
def safe_file_operation(filename: str, operation: str) -> Optional[str]:
"""Safely perform file operations."""
try:
if operation == 'read':
with open(filename, 'r') as f:
return f.read()
elif operation == 'write':
with open(filename, 'w') as f:
f.write("Sample content")
return "File written successfully"
else:
raise ValueError(f"Unknown operation: {operation}")
except FileNotFoundError:
print(f"File not found: {filename}")
return None
except PermissionError:
print(f"Permission denied for file: {filename}")
return None
except Exception as e:
print(f"Unexpected error: {e}")
return None
# 7. Use __all__ to control what gets imported
__all__ = [
'process_user_data',
'calculate_statistics',
'safe_file_operation',
'DEFAULT_TIMEOUT',
'MAX_RETRIES',
'SUPPORTED_FORMATS'
]
Key Takeaways
- Modules are single files containing Python code
- Packages are collections of modules organized in directories
- Import statements allow you to use code from other modules/packages
__name__helps distinguish between direct execution and import- Virtual environments isolate project dependencies
- Requirements files help manage and reproduce environments
- Standard library provides many useful modules out of the box
- Best practices include clear naming, documentation, and error handling
Next Steps
In the next lesson, we'll explore Object-Oriented Programming - how to create classes, work with objects, and implement inheritance and polymorphism.