Previous | Tutorial index | Next
Welcome to the capstone tutorial! Over the previous six tutorials, you’ve built a solid foundation in Python’s core data structures: strings, lists, tuples, sets, dictionaries, and file I/O. Now it’s time to bring everything together. You will learn how to choose the right structure for a given problem, design data processing pipelines, and build complete mini‑systems that read, manipulate, and store data. This tutorial is packed with realistic examples, quizzes, exercises, and challenging homework problems to cement your skills.
The first step in any data‑oriented problem is deciding which structure best fits your needs. Here’s a quick reference table:
| Task | Recommended Structure | Why? |
|---|---|---|
| Store an ordered collection of items that may change (add/remove/modify) | list |
Mutable, ordered, fast indexing. |
| Store a fixed collection that should never change (e.g., coordinates, constants) | tuple |
Immutable, more memory‑efficient, hashable (can be a dict key). |
| Store unique items and test membership frequently, or perform set operations | set |
O(1) membership, automatically eliminates duplicates. |
| Map keys to values for fast lookups (e.g., user ID → user data) | dict |
O(1) key lookup, flexible keys (immutable types). |
| Preserve insertion order and also need key‑value mapping | dict (Python 3.7+) |
Order is now a guaranteed feature. |
| Need to combine multiple data structures (e.g., list of dicts) | Use nested structures | Reflects complex relationships. |
Is order important?
├─ Yes → Is data mutable?
│ ├─ Yes → list
│ └─ No → tuple
└─ No → Are items unique?
├─ Yes → set
└─ No → Need key‑value mapping?
├─ Yes → dict
└─ No → list (if no uniqueness, but order not needed) – but usually you want uniqueness for sets; otherwise list is fine.
dict (column → value) stored in a list of rows.set to track seen items.dict (item → count).tuple (e.g., (name, age, city)).dict mapping session ID to user data.list of task strings (add/remove).A pipeline is a sequence of steps that transform raw input into a desired output. Typical phases:
We’ll take the basic word counter and enhance it with cleaning steps.
import string
def word_frequency_pipeline(filename, stop_words=None, top_n=None):
"""
Read a text file, clean each word (lowercase, remove punctuation),
ignore stop words (if provided), count frequencies, and optionally
return the top N words.
"""
# Acquisition
with open(filename, 'r', encoding='utf-8') as f:
lines = f.readlines()
word_counts = {}
# Process each line
for line in lines:
# Cleaning: lower case and remove punctuation
# We'll use str.translate to remove punctuation efficiently
translator = str.maketrans('', '', string.punctuation)
cleaned_line = line.translate(translator).lower()
words = cleaned_line.split()
# Transformation: count
for word in words:
# Check stop words
if stop_words and word in stop_words:
continue
word_counts[word] = word_counts.get(word, 0) + 1
# Analysis: sort by frequency descending
sorted_counts = sorted(word_counts.items(), key=lambda x: x[1], reverse=True)
# Output
if top_n:
return sorted_counts[:top_n]
return sorted_counts
Example usage:
stop = {'the', 'a', 'an', 'and', 'or', 'but', 'for', 'nor', 'on', 'at', 'to', 'by'}
result = word_frequency_pipeline('sample.txt', stop_words=stop, top_n=10)
for word, count in result:
print(f"{word}: {count}")
For large files, avoid readlines() (which loads all lines into memory). Instead, iterate over the file object directly:
with open('large_file.txt', 'r') as f:
for line in f: # reads one line at a time
process(line)
This is memory‑efficient and should be your default for text processing.
We’ll design a simple system to manage student records. Each student has a unique ID, name, age, and list of courses (or grades). We'll store data in a dictionary keyed by ID, and persist to a CSV file.
students: dict[int, dict] where each inner dict has keys: id, name, age, courses (list of course names) or grades (dict course→grade). We'll keep it simple: store name, age, and a list of courses.import csv
import os
STUDENT_FILE = 'students.csv'
def load_students():
"""Load student data from CSV file into a dict keyed by ID."""
students = {}
if not os.path.exists(STUDENT_FILE):
return students
with open(STUDENT_FILE, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
for row in reader:
# Convert ID to int, courses from string to list
sid = int(row['id'])
courses = row['courses'].split(';') if row['courses'] else []
students[sid] = {
'id': sid,
'name': row['name'],
'age': int(row['age']),
'courses': courses
}
return students
def save_students(students):
"""Save students dict to CSV file."""
with open(STUDENT_FILE, 'w', newline='', encoding='utf-8') as f:
fieldnames = ['id', 'name', 'age', 'courses']
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
for sid, info in students.items():
# Convert courses list to semicolon-separated string
courses_str = ';'.join(info['courses'])
writer.writerow({
'id': sid,
'name': info['name'],
'age': info['age'],
'courses': courses_str
})
def add_student(students, name, age, courses=None):
"""Add a new student with auto-incremented ID."""
if not students:
new_id = 1
else:
new_id = max(students.keys()) + 1
students[new_id] = {
'id': new_id,
'name': name,
'age': age,
'courses': courses or []
}
save_students(students)
return new_id
def delete_student(students, sid):
"""Remove a student by ID."""
if sid in students:
del students[sid]
save_students(students)
return True
return False
def update_student(students, sid, **kwargs):
"""Update fields of a student. kwargs can be 'name', 'age', 'courses'."""
if sid not in students:
raise KeyError(f"Student {sid} not found")
for key, value in kwargs.items():
if key in students[sid]:
students[sid][key] = value
save_students(students)
def search_students(students, query):
"""Search by name (case-insensitive partial match)."""
query_lower = query.lower()
results = []
for info in students.values():
if query_lower in info['name'].lower():
results.append(info)
return results
You can build a simple REPL (Read‑Eval‑Print Loop) that uses these functions. This demonstrates a complete system.
Given a CSV with columns city,date,temperature, compute:
Data structure: Use a dict city → list of temps, or city → total and count.
(product_id, quantity, price).set for unique product IDs.dict for inventory: product_id → {name, price, stock}.| Operation | List | Set | Dict |
|---|---|---|---|
| Indexing | O(1) | N/A | O(1) (by key) |
| Search (in) | O(n) | O(1) | O(1) (keys) |
| Insert/Delete | O(n) (unless at end) | O(1) | O(1) |
| Sorting | O(n log n) | N/A | N/A (can sort keys) |
set for membership tests (e.g., if x in my_set).dict for lookups (e.g., value = my_dict[key]).list.index() – it's O(n); consider a dict mapping item → index if you need many lookups.with to auto‑close.Always anticipate failures: missing files, corrupt data, invalid input. Use try/except to fail gracefully.
Write small test functions or use doctest to verify your functions. For example:
def test_word_frequency():
# create a temporary file or use a string
pass
Which data structure would you use to store a collection of unique user IDs for fast membership testing?
listtuplesetdictYou need to store product information (ID, name, price) and frequently look up by ID. What structure is best?
list of tuplestuple of listsdict with ID as key and a dict/value as valueset of product objectsTrue or False: When reading a large text file, it is always better to use file.read() to load all content at once for faster processing.
What is the time complexity of checking if an element is in a set?
Given a list of words, you want to remove duplicates while preserving the original order. Which approach is best?
list.count() to filter.sort() and then unique.What does the str.translate method do in the word frequency example?
When saving a dictionary to a CSV file, why is it important to convert lists (like courses) to a string?
Which of the following is NOT a valid key type for a dictionary?
intstrtuplelistIn the student management system, the load_students function returns an empty dict if the file doesn't exist. This is an example of:
What is the purpose of the with statement when opening files?
Exercise 1: Log File Summary
Write a program that reads a server log file (timestamp,level,message), counts levels, prints summary, and writes ERROR lines to a separate file.
Exercise 2: Unique Word Set
Write a function unique_words(filename) that returns a set of all unique words (case‑insensitive, ignoring punctuation).
Exercise 3: Dictionary Inversion
Given a dict mapping students to courses (as lists), return an inverted dict mapping each course to a list of students.
Exercise 4: CSV Filter and Sum
Read a CSV with product,quantity,price, compute total revenue, filter out rows with quantity < 5, and write a new CSV with an extra total column.
Exercise 5: Shopping Cart with Set
Implement a shopping cart as a set of item IDs, with functions to add, remove, display, and compute total price using a product price dict.
1. Inventory Management System
Design a complete inventory system with a dict mapping product ID to details, functions for CRUD, sale processing, and CSV persistence.
2. Anagram Groups with File I/O
Read words from a file, group anagrams, output groups to a file, and print the group with the most words.
3. Data Cleaning and Analysis
Clean a messy CSV, compute average salary by city, and write a clean CSV with salary grade.
4. Building a Simple Search Engine (In‑Memory)
Build an inverted index (word → set of document IDs) and implement a search for AND queries. Persist to JSON.
5. Simulating a Bank Account System
Design a system with accounts, transactions, deposit/withdraw/transfer, and file persistence.
dict for inventory, csv.DictReader/DictWriter. For sale, loop, check stock, update.''.join(sorted(word)); use a dictionary mapping key to list.csv.reader; handle exceptions with try/except; use strip(), isdigit(), etc.{word: set(doc_ids)}; search: intersect sets for all words.datetime for dates; save accounts to CSV, transactions to separate CSV with account number.In this capstone tutorial, you have:
You are now ready to tackle real‑world challenges that require thoughtful data organization and manipulation. Keep practicing, and always think about the trade‑offs between different structures. The more you work with them, the more intuitive your choices will become.
What’s next? Continue building your own projects, explore more advanced libraries like pandas for data analysis, or dive into object‑oriented programming to structure larger systems.
Happy problem solving!