Table of Contents
Understanding Python Modules and Packages
Modules and packages help organize code into reusable components, making your programs more maintainable and scalable.
What are Modules?
A module is a file containing Python code that can define functions, classes, and variables. It can also include runnable code.
# Create a file called math_utils.py
# math_utils.py
"""
Mathematical utility functions
"""
import math
PI = 3.14159
def circle_area(radius):
"""Calculate the area of a circle"""
return PI * radius ** 2
def circle_circumference(radius):
"""Calculate the circumference of a circle"""
return 2 * PI * radius
def factorial(n):
"""Calculate factorial of n"""
if n < 0:
raise ValueError("Factorial is not defined for negative numbers")
if n == 0 or n == 1:
return 1
return n * factorial(n - 1)
def is_prime(n):
"""Check if a number is prime"""
if n < 2:
return False
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return False
return True
def fibonacci(n):
"""Generate first n fibonacci numbers"""
if n <= 0:
return []
elif n == 1:
return [0]
elif n == 2:
return [0, 1]
fib = [0, 1]
for i in range(2, n):
fib.append(fib[i-1] + fib[i-2])
return fib
# Code that runs when module is executed directly
if __name__ == "__main__":
print("Testing math_utils module:")
print(f"Circle area (radius=5): {circle_area(5)}")
print(f"Factorial of 5: {factorial(5)}")
print(f"Is 17 prime? {is_prime(17)}")
print(f"First 10 Fibonacci numbers: {fibonacci(10)}")
Importing Modules
# Different ways to import modules
# 1. Import entire module
import math_utils
print(math_utils.circle_area(5))
print(math_utils.PI)
# 2. Import specific functions
from math_utils import circle_area, factorial
print(circle_area(3))
print(factorial(4))
# 3. Import with alias
import math_utils as mu
print(mu.is_prime(17))
# 4. Import specific functions with alias
from math_utils import fibonacci as fib
print(fib(8))
# 5. Import all (not recommended for large modules)
from math_utils import *
print(circle_circumference(4))
# 6. Import built-in modules
import os
import sys
import datetime
import random
print(f"Current directory: {os.getcwd()}")
print(f"Python version: {sys.version}")
print(f"Current time: {datetime.datetime.now()}")
print(f"Random number: {random.randint(1, 100)}")
Module Search Path
import sys
# Python searches for modules in these locations (in order):
print("Module search path:")
for i, path in enumerate(sys.path, 1):
print(f"{i}. {path}")
# Add custom path to module search
sys.path.append("/path/to/your/modules")
# Check if a module can be imported
def can_import(module_name):
try:
__import__(module_name)
return True
except ImportError:
return False
print(f"Can import 'requests': {can_import('requests')}")
print(f"Can import 'math_utils': {can_import('math_utils')}")
# Get module information
import math_utils
print(f"Module name: {math_utils.__name__}")
print(f"Module file: {math_utils.__file__}")
print(f"Module doc: {math_utils.__doc__}")
Creating Packages
A package is a directory containing multiple modules. It must have an __init__.py
file.
# Package structure:
# my_package/
# __init__.py
# math_operations.py
# string_operations.py
# data_structures.py
# my_package/__init__.py
"""
My custom package for various utilities
"""
__version__ = "1.0.0"
__author__ = "Your Name"
# Import key functions to package level
from .math_operations import add, multiply
from .string_operations import reverse_string, count_words
# Package-level function
def get_package_info():
return {
"name": __name__,
"version": __version__,
"author": __author__
}
# my_package/math_operations.py
"""
Mathematical operations module
"""
def add(a, b):
"""Add two numbers"""
return a + b
def subtract(a, b):
"""Subtract two numbers"""
return a - b
def multiply(a, b):
"""Multiply two numbers"""
return a * b
def divide(a, b):
"""Divide two numbers"""
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
def power(base, exponent):
"""Calculate base raised to exponent"""
return base ** exponent
# my_package/string_operations.py
"""
String manipulation operations
"""
def reverse_string(s):
"""Reverse a string"""
return s[::-1]
def count_words(text):
"""Count words in text"""
return len(text.split())
def capitalize_words(text):
"""Capitalize each word"""
return " ".join(word.capitalize() for word in text.split())
def remove_duplicates(text):
"""Remove duplicate characters"""
return "".join(dict.fromkeys(text))
# my_package/data_structures.py
"""
Custom data structure implementations
"""
class Stack:
"""Simple stack implementation"""
def __init__(self):
self.items = []
def push(self, item):
"""Add item to top of stack"""
self.items.append(item)
def pop(self, item):
"""Remove and return top item"""
if self.is_empty():
raise IndexError("Stack is empty")
return self.items.pop()
def peek(self):
"""Return top item without removing"""
if self.is_empty():
raise IndexError("Stack is empty")
return self.items[-1]
def is_empty(self):
"""Check if stack is empty"""
return len(self.items) == 0
def size(self):
"""Return stack size"""
return len(self.items)
class Queue:
"""Simple queue implementation"""
def __init__(self):
self.items = []
def enqueue(self, item):
"""Add item to rear of queue"""
self.items.append(item)
def dequeue(self):
"""Remove and return front item"""
if self.is_empty():
raise IndexError("Queue is empty")
return self.items.pop(0)
def front(self):
"""Return front item without removing"""
if self.is_empty():
raise IndexError("Queue is empty")
return self.items[0]
def is_empty(self):
"""Check if queue is empty"""
return len(self.items) == 0
def size(self):
"""Return queue size"""
return len(self.items)
Using Packages
# Import from package
import my_package
# Use package-level imports
result = my_package.add(5, 3)
print(f"5 + 3 = {result}")
reversed_text = my_package.reverse_string("Hello")
print(f"Reversed: {reversed_text}")
# Get package info
info = my_package.get_package_info()
print(f"Package info: {info}")
# Import specific modules
from my_package import math_operations, string_operations, data_structures
# Use module functions
print(f"10 * 4 = {math_operations.multiply(10, 4)}")
print(f"Word count: {string_operations.count_words('Hello world Python')}")
# Use custom data structures
stack = data_structures.Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print(f"Stack size: {stack.size()}")
print(f"Top item: {stack.peek()}")
queue = data_structures.Queue()
queue.enqueue("first")
queue.enqueue("second")
print(f"Queue front: {queue.front()}")
# Import with different syntax
from my_package.math_operations import power
from my_package.string_operations import capitalize_words
print(f"2^8 = {power(2, 8)}")
print(f"Capitalized: {capitalize_words('hello world')}")
Standard Library Modules
# Commonly used standard library modules
# os - Operating system interface
import os
print(f"Current directory: {os.getcwd()}")
print(f"Files in current directory: {os.listdir('.')}")
# sys - System-specific parameters
import sys
print(f"Python executable: {sys.executable}")
print(f"Command line arguments: {sys.argv}")
# datetime - Date and time handling
from datetime import datetime, timedelta, date
now = datetime.now()
print(f"Current time: {now}")
print(f"Tomorrow: {now + timedelta(days=1)}")
# random - Generate random numbers
import random
print(f"Random integer: {random.randint(1, 100)}")
print(f"Random choice: {random.choice(['apple', 'banana', 'orange'])}")
# json - JSON encoder and decoder
import json
data = {"name": "Alice", "age": 30}
json_string = json.dumps(data)
print(f"JSON string: {json_string}")
parsed_data = json.loads(json_string)
print(f"Parsed data: {parsed_data}")
# re - Regular expressions
import re
text = "The phone number is 123-456-7890"
pattern = r'd{3}-d{3}-d{4}'
match = re.search(pattern, text)
if match:
print(f"Found phone number: {match.group()}")
# collections - Specialized container datatypes
from collections import Counter, defaultdict, namedtuple
words = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple']
word_count = Counter(words)
print(f"Word count: {word_count}")
# Create named tuple
Person = namedtuple('Person', ['name', 'age', 'city'])
person = Person('Alice', 30, 'New York')
print(f"Person: {person.name}, {person.age}, {person.city}")
# itertools - Functions for creating iterators
import itertools
numbers = [1, 2, 3]
permutations = list(itertools.permutations(numbers))
print(f"Permutations: {permutations}")
combinations = list(itertools.combinations(numbers, 2))
print(f"Combinations: {combinations}")
Third-Party Packages
# Installing third-party packages with pip
# pip install requests numpy pandas matplotlib
# Example usage (if packages are installed)
# requests - HTTP library
try:
import requests
response = requests.get('https://api.github.com/users/octocat')
if response.status_code == 200:
data = response.json()
print(f"GitHub user: {data['name']}")
except ImportError:
print("requests library not installed")
# numpy - Numerical computing
try:
import numpy as np
arr = np.array([1, 2, 3, 4, 5])
print(f"NumPy array: {arr}")
print(f"Array mean: {np.mean(arr)}")
except ImportError:
print("numpy library not installed")
# Check installed packages
import pkg_resources
installed_packages = [d.project_name for d in pkg_resources.working_set]
print(f"Installed packages: {sorted(installed_packages)[:10]}...") # Show first 10
Module Documentation and Help
# Getting help and documentation
# Built-in help function
help(len) # Help for built-in function
help(str.split) # Help for string method
# Module documentation
import math
help(math) # Full module documentation
help(math.sqrt) # Specific function help
# Using dir() to explore modules
print("Math module contents:")
print(dir(math))
# Module attributes
print(f"Math module name: {math.__name__}")
print(f"Math module doc: {math.__doc__[:100]}...")
# Inspect module
import inspect
def example_function(x, y=10):
"""Example function with parameters"""
return x + y
# Get function signature
sig = inspect.signature(example_function)
print(f"Function signature: {sig}")
# Get source code (if available)
try:
source = inspect.getsource(example_function)
print(f"Source code:
{source}")
except:
print("Source code not available")
Practice Exercise
Create a comprehensive text analysis package:
# text_analyzer/
# __init__.py
# statistics.py
# sentiment.py
# readability.py
# text_analyzer/__init__.py
"""
Text Analysis Package
A comprehensive package for analyzing text content including
statistics, sentiment analysis, and readability metrics.
"""
__version__ = "1.0.0"
__author__ = "Your Name"
from .statistics import TextStatistics
from .sentiment import SentimentAnalyzer
from .readability import ReadabilityAnalyzer
class TextAnalyzer:
"""Main text analyzer class combining all analysis tools"""
def __init__(self):
self.stats = TextStatistics()
self.sentiment = SentimentAnalyzer()
self.readability = ReadabilityAnalyzer()
def analyze(self, text):
"""Perform comprehensive text analysis"""
return {
'statistics': self.stats.analyze(text),
'sentiment': self.sentiment.analyze(text),
'readability': self.readability.analyze(text)
}
# text_analyzer/statistics.py
"""
Text statistics analysis module
"""
import re
from collections import Counter
class TextStatistics:
"""Calculate various text statistics"""
def __init__(self):
self.word_pattern = re.compile(r'w+')
self.sentence_pattern = re.compile(r'[.!?]+s*')
def count_characters(self, text):
"""Count total characters"""
return len(text)
def count_characters_no_spaces(self, text):
"""Count characters excluding spaces"""
return len(re.sub(r's', '', text))
def count_words(self, text):
"""Count total words"""
return len(self.word_pattern.findall(text))
def count_sentences(self, text):
"""Count sentences"""
sentences = self.sentence_pattern.split(text)
return len([s for s in sentences if s.strip()])
def count_paragraphs(self, text):
"""Count paragraphs"""
paragraphs = text.split('
')
return len([p for p in paragraphs if p.strip()])
def average_word_length(self, text):
"""Calculate average word length"""
words = self.word_pattern.findall(text)
if not words:
return 0
return sum(len(word) for word in words) / len(words)
def average_sentence_length(self, text):
"""Calculate average sentence length in words"""
word_count = self.count_words(text)
sentence_count = self.count_sentences(text)
return word_count / sentence_count if sentence_count > 0 else 0
def word_frequency(self, text, top_n=10):
"""Get most frequent words"""
words = [word.lower() for word in self.word_pattern.findall(text)]
return Counter(words).most_common(top_n)
def analyze(self, text):
"""Perform complete statistical analysis"""
return {
'character_count': self.count_characters(text),
'character_count_no_spaces': self.count_characters_no_spaces(text),
'word_count': self.count_words(text),
'sentence_count': self.count_sentences(text),
'paragraph_count': self.count_paragraphs(text),
'average_word_length': round(self.average_word_length(text), 2),
'average_sentence_length': round(self.average_sentence_length(text), 2),
'top_words': self.word_frequency(text)
}
# text_analyzer/sentiment.py
"""
Simple sentiment analysis module
"""
class SentimentAnalyzer:
"""Basic sentiment analysis using word lists"""
def __init__(self):
# Simple word lists for demonstration
self.positive_words = {
'good', 'great', 'excellent', 'amazing', 'wonderful',
'fantastic', 'awesome', 'brilliant', 'outstanding', 'superb',
'happy', 'joy', 'love', 'like', 'enjoy', 'pleased',
'satisfied', 'delighted', 'thrilled', 'excited'
}
self.negative_words = {
'bad', 'terrible', 'awful', 'horrible', 'disgusting',
'hate', 'dislike', 'angry', 'sad', 'disappointed',
'frustrated', 'annoyed', 'upset', 'worried', 'concerned',
'poor', 'worst', 'fail', 'failed', 'wrong'
}
def get_word_sentiment(self, word):
"""Get sentiment score for a single word"""
word = word.lower()
if word in self.positive_words:
return 1
elif word in self.negative_words:
return -1
else:
return 0
def analyze(self, text):
"""Analyze sentiment of text"""
words = text.lower().split()
positive_count = 0
negative_count = 0
total_sentiment = 0
for word in words:
# Remove punctuation
clean_word = '.join(c for c in word if c.isalnum())
sentiment = self.get_word_sentiment(clean_word)
if sentiment > 0:
positive_count += 1
elif sentiment < 0:
negative_count += 1
total_sentiment += sentiment
# Calculate overall sentiment
if total_sentiment > 0:
overall = 'positive'
elif total_sentiment < 0:
overall = 'negative'
else:
overall = 'neutral'
return {
'overall_sentiment': overall,
'sentiment_score': total_sentiment,
'positive_words': positive_count,
'negative_words': negative_count,
'total_words': len(words)
}
# text_analyzer/readability.py
"""
Text readability analysis module
"""
import re
class ReadabilityAnalyzer:
"""Calculate readability metrics"""
def __init__(self):
self.sentence_pattern = re.compile(r'[.!?]+s*')
self.word_pattern = re.compile(r'w+')
self.syllable_pattern = re.compile(r'[aeiouyAEIOUY]+')
def count_syllables(self, word):
"""Estimate syllable count in a word"""
# Simple syllable counting (not perfect but reasonable)
word = word.lower()
syllables = len(self.syllable_pattern.findall(word))
# Adjust for silent e
if word.endswith('e') and syllables > 1:
syllables -= 1
# Ensure at least 1 syllable
return max(1, syllables)
def flesch_reading_ease(self, text):
"""Calculate Flesch Reading Ease score"""
words = self.word_pattern.findall(text)
sentences = len([s for s in self.sentence_pattern.split(text) if s.strip()])
if not words or sentences == 0:
return 0
total_syllables = sum(self.count_syllables(word) for word in words)
# Flesch Reading Ease formula
score = (206.835 -
(1.015 * len(words) / sentences) -
(84.6 * total_syllables / len(words)))
return round(score, 2)
def flesch_kincaid_grade(self, text):
"""Calculate Flesch-Kincaid Grade Level"""
words = self.word_pattern.findall(text)
sentences = len([s for s in self.sentence_pattern.split(text) if s.strip()])
if not words or sentences == 0:
return 0
total_syllables = sum(self.count_syllables(word) for word in words)
# Flesch-Kincaid Grade Level formula
grade = (0.39 * len(words) / sentences) + (11.8 * total_syllables / len(words)) - 15.59
return round(max(0, grade), 2)
def get_reading_level(self, flesch_score):
"""Convert Flesch score to reading level description"""
if flesch_score >= 90:
return "Very Easy"
elif flesch_score >= 80:
return "Easy"
elif flesch_score >= 70:
return "Fairly Easy"
elif flesch_score >= 60:
return "Standard"
elif flesch_score >= 50:
return "Fairly Difficult"
elif flesch_score >= 30:
return "Difficult"
else:
return "Very Difficult"
# Example usage
analyzer = ReadabilityAnalyzer()
text = "Python is a powerful programming language. It is easy to learn and use."
print(f"Word count: {analyzer.count_words(text)}")
print(f"Character count: {analyzer.count_characters(text)}")
print(f"Flesch Reading Ease: {analyzer.flesch_reading_ease(text)}")
print(f"Reading Level: {analyzer.get_reading_level(analyzer.flesch_reading_ease(text))}")