4.2 Data Structures in Python (Required Set)¶
From Theory to Implementation¶
In Section 3.4, we learned about different data structures conceptually. Now it’s time to implement them in Python! This section focuses on the specific data structures required for the NSW HSC syllabus, showing you how to use them effectively in your programs.
The required data structures are:
-
Single and multidimensional arrays (Python lists)
-
Lists with operations
-
Trees (simple nested representations)
-
Stacks (using list operations)
-
Hash tables (Python dictionaries)
-
Sequential files (CSV)
Single and Multidimensional Arrays → Python Lists¶
Single-Dimensional Arrays (1D Lists)¶
Python lists are our implementation of single-dimensional arrays. They store multiple values in a single variable, accessible by index.
# Creating and using 1D arrays (lists)
student_grades = [85, 92, 78, 96, 88]
student_names = ["Alice", "Bob", "Charlie", "Diana", "Eve"]
# Accessing elements by index
first_grade = student_grades[0] # 85
last_name = student_names[-1] # "Eve"
print(f"First student: {student_names[0]} with grade {student_grades[0]}")
# Common operations
print(f"Total students: {len(student_names)}")
print(f"Highest grade: {max(student_grades)}")
print(f"Average grade: {sum(student_grades) / len(student_grades):.1f}")
Array Processing Patterns¶
def process_exam_scores(scores):
"""Demonstrate common array processing patterns."""
# Pattern 1: Find maximum value and its position
max_score = scores[0]
max_position = 0
for i in range(len(scores)):
if scores[i] > max_score:
max_score = scores[i]
max_position = i
print(f"Highest score: {max_score} at position {max_position}")
# Pattern 2: Count elements meeting a condition
passing_count = 0
for score in scores:
if score >= 60:
passing_count += 1
print(f"Students passing: {passing_count} out of {len(scores)}")
# Pattern 3: Create a new array based on conditions
letter_grades = []
for score in scores:
if score >= 90:
letter_grades.append("A")
elif score >= 80:
letter_grades.append("B")
elif score >= 70:
letter_grades.append("C")
elif score >= 60:
letter_grades.append("D")
else:
letter_grades.append("F")
return letter_grades
# Test the processing function
exam_scores = [95, 87, 76, 92, 58, 83]
grades = process_exam_scores(exam_scores)
print("Letter grades:", grades)
Multidimensional Arrays (2D Lists)¶
Two-dimensional arrays represent tables, grids, or matrices using lists of lists.
# Creating a 2D array - grade book for 4 students, 3 subjects
# Each row represents a student, each column represents a subject
grade_book = [
[85, 92, 78], # Alice: Math, Science, English
[91, 87, 94], # Bob: Math, Science, English
[76, 83, 81], # Charlie: Math, Science, English
[88, 95, 89] # Diana: Math, Science, English
]
student_names = ["Alice", "Bob", "Charlie", "Diana"]
subject_names = ["Math", "Science", "English"]
# Accessing specific elements
alice_math = grade_book[0][0] # Row 0, Column 0
bob_english = grade_book[1][2] # Row 1, Column 2
print(f"Alice's Math grade: {alice_math}")
print(f"Bob's English grade: {bob_english}")
2D Array Operations¶
def analyze_grade_book(grades, students, subjects):
"""Analyze a 2D grade book array."""
print("=== Grade Book Analysis ===")
# Display the complete grade book
print("\nComplete Grade Book:")
print("Student".ljust(12), end="")
for subject in subjects:
print(subject.ljust(10), end="")
print()
for i in range(len(students)):
print(students[i].ljust(12), end="")
for j in range(len(subjects)):
print(str(grades[i][j]).ljust(10), end="")
print()
# Calculate student averages (process rows)
print("\nStudent Averages:")
for i in range(len(students)):
total = sum(grades[i])
average = total / len(grades[i])
print(f"{students[i]}: {average:.1f}")
# Calculate subject averages (process columns)
print("\nSubject Averages:")
for j in range(len(subjects)):
total = 0
for i in range(len(students)):
total += grades[i][j]
average = total / len(students)
print(f"{subjects[j]}: {average:.1f}")
# Analyze our grade book
analyze_grade_book(grade_book, student_names, subject_names)
Dynamic 2D Array Creation¶
def create_seating_chart(rows, seats_per_row):
"""Create a dynamic seating chart for a theater."""
# Create 2D array with all seats initially available (False)
seating_chart = []
for row in range(rows):
seat_row = []
for seat in range(seats_per_row):
seat_row.append(False) # False = available, True = occupied
seating_chart.append(seat_row)
return seating_chart
def display_seating_chart(chart):
"""Display the seating chart visually."""
print("Seating Chart (O = occupied, . = available):")
print(" ", end="")
# Print seat numbers header
for seat in range(len(chart[0])):
print(f"{seat+1:2}", end="")
print()
# Print each row
for row in range(len(chart)):
print(f"{row+1:2} ", end="")
for seat in range(len(chart[row])):
symbol = "O" if chart[row][seat] else "."
print(f"{symbol:2}", end="")
print()
def book_seat(chart, row, seat):
"""Book a seat if available."""
if 0 <= row < len(chart) and 0 <= seat < len(chart[0]):
if not chart[row][seat]:
chart[row][seat] = True
print(f"Seat {row+1}-{seat+1} booked successfully!")
return True
else:
print(f"Seat {row+1}-{seat+1} is already occupied!")
return False
else:
print("Invalid seat location!")
return False
# Create and use the seating system
theater = create_seating_chart(4, 6)
display_seating_chart(theater)
# Book some seats
book_seat(theater, 0, 2) # Row 1, Seat 3
book_seat(theater, 1, 1) # Row 2, Seat 2
book_seat(theater, 0, 2) # Try to book same seat again
print("\nUpdated seating chart:")
display_seating_chart(theater)
Lists (Python List Operations)¶
Essential List Operations¶
# Creating lists
numbers = [10, 20, 30, 40, 50]
fruits = ["apple", "banana", "cherry"]
mixed = [1, "hello", 3.14, True]
# Adding elements
fruits.append("date") # Add to end: ["apple", "banana", "cherry", "date"]
fruits.insert(1, "blueberry") # Insert at position 1
numbers.extend([60, 70]) # Add multiple elements
print("After additions:", fruits)
print("Extended numbers:", numbers)
# Removing elements
fruits.remove("banana") # Remove first occurrence
last_fruit = fruits.pop() # Remove and return last element
second_fruit = fruits.pop(1) # Remove and return element at index 1
print("After removals:", fruits)
print(f"Removed: {last_fruit} and {second_fruit}")
# Finding and checking
if "apple" in fruits:
position = fruits.index("apple")
print(f"Apple found at position {position}")
count_a = fruits.count("apple")
print(f"Number of apples: {count_a}")
List Manipulation Techniques¶
def manage_shopping_list():
"""Interactive shopping list manager."""
shopping_list = []
while True:
print(f"\nShopping List ({len(shopping_list)} items):")
for i, item in enumerate(shopping_list):
print(f"{i+1}. {item}")
print("\nOptions:")
print("1. Add item")
print("2. Remove item")
print("3. Move item")
print("4. Clear list")
print("5. Exit")
choice = input("Choose option: ")
if choice == "1":
item = input("Enter item to add: ")
shopping_list.append(item)
print(f"Added '{item}' to the list")
elif choice == "2":
if shopping_list:
item = input("Enter item to remove: ")
if item in shopping_list:
shopping_list.remove(item)
print(f"Removed '{item}' from the list")
else:
print(f"'{item}' not found in list")
else:
print("List is empty!")
elif choice == "3":
if len(shopping_list) >= 2:
try:
old_pos = int(input("Current position (1-based): ")) - 1
new_pos = int(input("New position (1-based): ")) - 1
if 0 <= old_pos < len(shopping_list) and 0 <= new_pos < len(shopping_list):
item = shopping_list.pop(old_pos)
shopping_list.insert(new_pos, item)
print(f"Moved '{item}' to position {new_pos + 1}")
else:
print("Invalid positions!")
except ValueError:
print("Please enter valid numbers!")
else:
print("Need at least 2 items to move!")
elif choice == "4":
shopping_list.clear()
print("List cleared!")
elif choice == "5":
break
else:
print("Invalid choice!")
# Run the shopping list manager (uncomment to test)
# manage_shopping_list()
List Sorting and Searching¶
def demonstrate_list_algorithms():
"""Show sorting and searching algorithms with lists."""
# Sample data
student_scores = [78, 92, 85, 67, 91, 73, 88, 95]
student_names = ["Alice", "Bob", "Charlie", "Diana", "Eve", "Frank", "Grace", "Henry"]
print("Original scores:", student_scores)
# Bubble sort implementation
def bubble_sort(arr):
"""Sort array using bubble sort algorithm."""
n = len(arr)
sorted_arr = arr.copy() # Don't modify original
for i in range(n):
for j in range(0, n - i - 1):
if sorted_arr[j] > sorted_arr[j + 1]:
# Swap elements
sorted_arr[j], sorted_arr[j + 1] = sorted_arr[j + 1], sorted_arr[j]
return sorted_arr
# Linear search implementation
def linear_search(arr, target):
"""Find target in array using linear search."""
for i in range(len(arr)):
if arr[i] == target:
return i
return -1
# Sort the scores
sorted_scores = bubble_sort(student_scores)
print("Sorted scores:", sorted_scores)
# Search for a score
target = 85
position = linear_search(student_scores, target)
if position != -1:
print(f"Score {target} found at position {position} (student: {student_names[position]})")
else:
print(f"Score {target} not found")
demonstrate_list_algorithms()
Trees (Simple Nested Dict/List Representation)¶
Tree Structures Using Nested Dictionaries¶
# Simple family tree using nested dictionaries
family_tree = {
"name": "John (Grandfather)",
"children": [
{
"name": "Michael (Father)",
"children": [
{"name": "Sarah (Daughter)", "children": []},
{"name": "David (Son)", "children": []}
]
},
{
"name": "Lisa (Aunt)",
"children": [
{"name": "Emma (Cousin)", "children": []}
]
}
]
}
def print_family_tree(person, level=0):
"""Print family tree with proper indentation."""
indent = " " * level
print(f"{indent}{person['name']}")
for child in person['children']:
print_family_tree(child, level + 1)
def count_family_members(person):
"""Count total number of family members."""
count = 1 # Count current person
for child in person['children']:
count += count_family_members(child)
return count
def find_person(tree, target_name):
"""Search for a person in the family tree."""
if target_name.lower() in tree['name'].lower():
return tree
for child in tree['children']:
result = find_person(child, target_name)
if result:
return result
return None
print("Family Tree:")
print_family_tree(family_tree)
print(f"\nTotal family members: {count_family_members(family_tree)}")
# Search for a person
search_name = "Sarah"
person = find_person(family_tree, search_name)
if person:
print(f"Found: {person['name']}")
else:
print(f"{search_name} not found in family tree")
File System Tree Structure¶
# File system representation using nested structure
file_system = {
"name": "root",
"type": "folder",
"contents": [
{
"name": "Documents",
"type": "folder",
"contents": [
{"name": "essay.txt", "type": "file", "size": 1024},
{"name": "report.pdf", "type": "file", "size": 2048},
{
"name": "Projects",
"type": "folder",
"contents": [
{"name": "project1.py", "type": "file", "size": 512}
]
}
]
},
{
"name": "Pictures",
"type": "folder",
"contents": [
{"name": "vacation.jpg", "type": "file", "size": 3072}
]
}
]
}
def display_file_system(item, level=0):
"""Display file system structure."""
indent = " " * level
if item["type"] == "folder":
print(f"{indent}📁 {item['name']}/")
if "contents" in item:
for content in item["contents"]:
display_file_system(content, level + 1)
else:
print(f"{indent}📄 {item['name']} ({item['size']} bytes)")
def calculate_folder_size(folder):
"""Calculate total size of a folder and its contents."""
if folder["type"] == "file":
return folder["size"]
total_size = 0
if "contents" in folder:
for item in folder["contents"]:
total_size += calculate_folder_size(item)
return total_size
print("File System Structure:")
display_file_system(file_system)
print(f"\nTotal size: {calculate_folder_size(file_system)} bytes")
Stacks (Implemented via Python List append/pop)¶
Stack Implementation Using Lists¶
class SimpleStack:
"""Stack implementation using Python list."""
def __init__(self):
self.items = []
def push(self, item):
"""Add item to top of stack."""
self.items.append(item)
print(f"Pushed: {item}")
def pop(self):
"""Remove and return top item from stack."""
if self.is_empty():
print("Stack is empty!")
return None
item = self.items.pop()
print(f"Popped: {item}")
return item
def peek(self):
"""Look at top item without removing it."""
if self.is_empty():
print("Stack is empty!")
return None
return self.items[-1]
def is_empty(self):
"""Check if stack is empty."""
return len(self.items) == 0
def size(self):
"""Get number of items in stack."""
return len(self.items)
def display(self):
"""Display stack contents."""
if self.is_empty():
print("Stack: empty")
else:
print(f"Stack (top -> bottom): {self.items[::-1]}")
# Demonstrate stack operations
print("=== Stack Demo ===")
stack = SimpleStack()
# Push items
stack.push("First")
stack.push("Second")
stack.push("Third")
stack.display()
# Peek at top
top_item = stack.peek()
print(f"Top item: {top_item}")
stack.display()
# Pop items
stack.pop()
stack.pop()
stack.display()
print(f"Stack size: {stack.size()}")
Practical Stack Applications¶
def bracket_checker(expression):
"""Check if brackets are properly matched using a stack."""
stack = []
opening = "([{"
closing = ")]}"
pairs = {"(": ")", "[": "]", "{": "}"}
for char in expression:
if char in opening:
stack.append(char)
elif char in closing:
if not stack:
return False, f"Closing '{char}' without opening bracket"
last_opening = stack.pop()
if pairs[last_opening] != char:
return False, f"Mismatched brackets: '{last_opening}' and '{char}'"
if stack:
return False, f"Unclosed bracket: '{stack[-1]}'"
return True, "All brackets properly matched"
def undo_redo_system():
"""Simple undo/redo system using two stacks."""
undo_stack = []
redo_stack = []
current_text = ""
def type_text(text):
nonlocal current_text
undo_stack.append(current_text) # Save current state
redo_stack.clear() # Clear redo history
current_text += text
print(f"Typed: '{text}' | Text: '{current_text}'")
def undo():
nonlocal current_text
if undo_stack:
redo_stack.append(current_text) # Save current for redo
current_text = undo_stack.pop()
print(f"Undo | Text: '{current_text}'")
else:
print("Nothing to undo")
def redo():
nonlocal current_text
if redo_stack:
undo_stack.append(current_text) # Save current for undo
current_text = redo_stack.pop()
print(f"Redo | Text: '{current_text}'")
else:
print("Nothing to redo")
# Demonstrate the system
print("\n=== Undo/Redo Demo ===")
type_text("Hello")
type_text(" World")
type_text("!")
undo()
undo()
type_text(" Python")
undo()
redo()
# Test bracket checker
test_expressions = [
"(a + b) * c",
"[(a + b) * c]",
"((a + b)",
"(a + b] * c",
"{[()]}"
]
print("=== Bracket Checker ===")
for expr in test_expressions:
valid, message = bracket_checker(expr)
print(f"'{expr}': {message}")
# Run undo/redo demo
undo_redo_system()
Hash Tables (Python Dict)¶
Dictionary Fundamentals¶
# Creating and using dictionaries
student_grades = {
"Alice": 92,
"Bob": 87,
"Charlie": 78,
"Diana": 95
}
# Accessing and modifying
print(f"Alice's grade: {student_grades['Alice']}")
student_grades["Eve"] = 89 # Add new student
student_grades["Bob"] = 90 # Update existing student
print("Updated grades:", student_grades)
# Safe access methods
alice_grade = student_grades.get("Alice", 0) # Returns 0 if not found
frank_grade = student_grades.get("Frank", 0)
print(f"Alice: {alice_grade}, Frank: {frank_grade}")
# Dictionary operations
print("All students:", list(student_grades.keys()))
print("All grades:", list(student_grades.values()))
print("Student-grade pairs:", list(student_grades.items()))
Advanced Dictionary Applications¶
def create_word_frequency_counter():
"""Count word frequencies in text using a dictionary."""
def count_words(text):
"""Count frequency of each word in text."""
# Convert to lowercase and split into words
words = text.lower().replace(",", "").replace(".", "").split()
word_count = {}
for word in words:
if word in word_count:
word_count[word] += 1
else:
word_count[word] = 1
return word_count
def display_word_frequencies(word_dict):
"""Display word frequencies sorted by count."""
# Convert to list of tuples and sort by frequency
word_list = list(word_dict.items())
# Simple sort by frequency (descending)
for i in range(len(word_list)):
for j in range(i + 1, len(word_list)):
if word_list[i][1] < word_list[j][1]:
word_list[i], word_list[j] = word_list[j], word_list[i]
print("Word Frequencies:")
for word, count in word_list[:10]: # Show top 10
print(f"'{word}': {count}")
# Test with sample text
sample_text = """
Python is a great programming language. Python is easy to learn.
Programming with Python is fun. Python has many useful libraries.
"""
word_frequencies = count_words(sample_text)
display_word_frequencies(word_frequencies)
def create_simple_database():
"""Simple student database using dictionaries."""
students_db = {}
def add_student(student_id, name, age, grade):
"""Add a student to the database."""
students_db[student_id] = {
"name": name,
"age": age,
"grade": grade
}
print(f"Added student: {name}")
def get_student(student_id):
"""Retrieve student information."""
return students_db.get(student_id, None)
def update_grade(student_id, new_grade):
"""Update a student's grade."""
if student_id in students_db:
students_db[student_id]["grade"] = new_grade
print(f"Updated grade for {students_db[student_id]['name']}")
else:
print("Student not found")
def list_all_students():
"""List all students in the database."""
print("\n=== Student Database ===")
for student_id, info in students_db.items():
print(f"ID: {student_id} | Name: {info['name']} | Age: {info['age']} | Grade: {info['grade']}")
def find_students_by_grade(min_grade):
"""Find students with grades above threshold."""
high_achievers = []
for student_id, info in students_db.items():
if info["grade"] >= min_grade:
high_achievers.append((student_id, info))
return high_achievers
# Populate database
add_student("S001", "Alice Johnson", 16, 92)
add_student("S002", "Bob Smith", 17, 87)
add_student("S003", "Charlie Brown", 16, 78)
add_student("S004", "Diana Prince", 17, 95)
list_all_students()
# Update a grade
update_grade("S002", 90)
# Find high achievers
top_students = find_students_by_grade(90)
print(f"\nStudents with grades >= 90:")
for student_id, info in top_students:
print(f"- {info['name']}: {info['grade']}")
# Run demonstrations
print("=== Word Frequency Counter ===")
create_word_frequency_counter()
print("\n=== Student Database ===")
create_simple_database()
Sequential Files (CSV Read/Write)¶
Basic CSV Operations¶
import csv
def create_student_csv():
"""Create a CSV file with student data."""
students = [
["Student_ID", "Name", "Age", "Grade", "Subject"],
["S001", "Alice Johnson", 16, 92, "Math"],
["S002", "Bob Smith", 17, 87, "Science"],
["S003", "Charlie Brown", 16, 78, "English"],
["S004", "Diana Prince", 17, 95, "Math"],
["S005", "Eve Wilson", 16, 89, "Science"]
]
# Write to CSV file
with open("students.csv", "w", newline="") as file:
writer = csv.writer(file)
writer.writerows(students)
print("Created students.csv with sample data")
def read_student_csv():
"""Read and display data from CSV file."""
try:
with open("students.csv", "r") as file:
reader = csv.reader(file)
print("Student Data from CSV:")
for row_num, row in enumerate(reader):
if row_num == 0:
print("Headers:", " | ".join(row))
print("-" * 50)
else:
print(f"Row {row_num}: {' | '.join(row)}")
except FileNotFoundError:
print("students.csv not found. Please create it first.")
def advanced_csv_operations():
"""Demonstrate advanced CSV operations with DictReader/DictWriter."""
# Sample data as list of dictionaries
students = [
{"student_id": "S001", "name": "Alice Johnson", "math": 92, "science": 88, "english": 85},
{"student_id": "S002", "name": "Bob Smith", "math": 87, "science": 91, "english": 83},
{"student_id": "S003", "name": "Charlie Brown", "math": 78, "science": 82, "english": 86},
{"student_id": "S004", "name": "Diana Prince", "math": 95, "science": 93, "english": 91}
]
# Write using DictWriter
fieldnames = ["student_id", "name", "math", "science", "english", "average"]
with open("student_grades.csv", "w", newline="") as file:
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
for student in students:
# Calculate average
avg = (student["math"] + student["science"] + student["english"]) / 3
student["average"] = round(avg, 1)
writer.writerow(student)
print("Created student_grades.csv with calculated averages")
# Read using DictReader
with open("student_grades.csv", "r") as file:
reader = csv.DictReader(file)
print("\nStudent Grades with Averages:")
print("Name".ljust(15), "Math".ljust(6), "Science".ljust(8), "English".ljust(8), "Average")
print("-" * 50)
for row in reader:
print(f"{row['name']:<15} {row['math']:<6} {row['science']:<8} {row['english']:<8} {row['average']}")
def csv_analysis_tools():
"""Tools for analyzing CSV data."""
def analyze_grades_csv(filename):
"""Analyze student grades from CSV file."""
try:
with open(filename, "r") as file:
reader = csv.DictReader(file)
students = list(reader)
if not students:
print("No data found in file")
return
# Calculate statistics
math_scores = [float(student["math"]) for student in students]
science_scores = [float(student["science"]) for student in students]
english_scores = [float(student["english"]) for student in students]
print(f"\n=== Analysis of {filename} ===")
print(f"Total students: {len(students)}")
# Subject averages
print(f"Math average: {sum(math_scores) / len(math_scores):.1f}")
print(f"Science average: {sum(science_scores) / len(science_scores):.1f}")
print(f"English average: {sum(english_scores) / len(english_scores):.1f}")
# Find top performer
for student in students:
if float(student["average"]) == max(float(s["average"]) for s in students):
print(f"Top performer: {student['name']} with {student['average']} average")
break
except FileNotFoundError:
print(f"File {filename} not found")
except Exception as e:
print(f"Error analyzing file: {e}")
def export_filtered_data(input_file, output_file, min_average):
"""Export students with average above threshold."""
try:
with open(input_file, "r") as infile, open(output_file, "w", newline="") as outfile:
reader = csv.DictReader(infile)
writer = csv.DictWriter(outfile, fieldnames=reader.fieldnames)
writer.writeheader()
count = 0
for row in reader:
if float(row["average"]) >= min_average:
writer.writerow(row)
count += 1
print(f"Exported {count} students with average >= {min_average} to {output_file}")
except FileNotFoundError:
print(f"Input file {input_file} not found")
except Exception as e:
print(f"Error filtering data: {e}")
# Run analysis tools
analyze_grades_csv("student_grades.csv")
export_filtered_data("student_grades.csv", "high_achievers.csv", 90.0)
# Demonstrate CSV operations
print("=== CSV Operations Demo ===")
create_student_csv()
read_student_csv()
print("\n=== Advanced CSV Operations ===")
advanced_csv_operations()
print("\n=== CSV Analysis Tools ===")
csv_analysis_tools()
Integrating Multiple Data Structures¶
Complete Student Management System¶
import csv
class StudentManagementSystem:
"""Complete system using multiple data structures."""
def __init__(self):
# Hash table for quick student lookup
self.students = {}
# 2D array for grade storage
self.subjects = ["Math", "Science", "English", "History"]
# Stack for undo operations
self.undo_stack = []
# Tree structure for class hierarchy
self.class_structure = {
"school": "Trinity Grammar",
"years": [
{
"year": 11,
"classes": ["11A", "11B", "11C"]
},
{
"year": 12,
"classes": ["12A", "12B", "12C"]
}
]
}
def add_student(self, student_id, name, year, class_name):
"""Add a new student to the system."""
# Save current state for undo
self.undo_stack.append(("add", student_id, self.students.copy()))
# Initialize grade array with zeros
grades = [0] * len(self.subjects)
self.students[student_id] = {
"name": name,
"year": year,
"class": class_name,
"grades": grades
}
print(f"Added student: {name} (ID: {student_id})")
def update_grade(self, student_id, subject_index, grade):
"""Update a student's grade for a specific subject."""
if student_id in self.students:
# Save current state for undo
self.undo_stack.append(("update", student_id, self.students[student_id]["grades"].copy()))
self.students[student_id]["grades"][subject_index] = grade
subject_name = self.subjects[subject_index]
student_name = self.students[student_id]["name"]
print(f"Updated {student_name}'s {subject_name} grade to {grade}")
else:
print("Student not found")
def calculate_average(self, student_id):
"""Calculate a student's average grade."""
if student_id in self.students:
grades = self.students[student_id]["grades"]
# Only include non-zero grades in average
valid_grades = [g for g in grades if g > 0]
if valid_grades:
return sum(valid_grades) / len(valid_grades)
else:
return 0
return None
def generate_report_card(self, student_id):
"""Generate a detailed report card for a student."""
if student_id not in self.students:
print("Student not found")
return
student = self.students[student_id]
print(f"\n=== Report Card ===")
print(f"Student: {student['name']}")
print(f"ID: {student_id}")
print(f"Year: {student['year']}")
print(f"Class: {student['class']}")
print("-" * 30)
for i, subject in enumerate(self.subjects):
grade = student["grades"][i]
if grade > 0:
print(f"{subject:<10}: {grade}")
else:
print(f"{subject:<10}: Not graded")
print("-" * 30)
average = self.calculate_average(student_id)
print(f"Average: {average:.1f}")
def save_to_csv(self, filename):
"""Save all student data to CSV file."""
fieldnames = ["student_id", "name", "year", "class"] + self.subjects + ["average"]
with open(filename, "w", newline="") as file:
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
for student_id, student in self.students.items():
row = {
"student_id": student_id,
"name": student["name"],
"year": student["year"],
"class": student["class"],
"average": round(self.calculate_average(student_id), 1)
}
# Add subject grades
for i, subject in enumerate(self.subjects):
row[subject] = student["grades"][i]
writer.writerow(row)
print(f"Data saved to {filename}")
def undo_last_operation(self):
"""Undo the last operation using the stack."""
if not self.undo_stack:
print("Nothing to undo")
return
operation, student_id, saved_state = self.undo_stack.pop()
if operation == "add":
# Remove the student that was added
if student_id in self.students:
del self.students[student_id]
print(f"Undid: Added student {student_id}")
elif operation == "update":
# Restore previous grades
if student_id in self.students:
self.students[student_id]["grades"] = saved_state
print(f"Undid: Grade update for student {student_id}")
def display_class_structure(self):
"""Display the school's class structure (tree)."""
print(f"\n=== {self.class_structure['school']} Structure ===")
for year_info in self.class_structure["years"]:
print(f"Year {year_info['year']}:")
for class_name in year_info["classes"]:
student_count = sum(1 for s in self.students.values()
if s["year"] == year_info["year"] and s["class"] == class_name)
print(f" {class_name}: {student_count} students")
# Demonstrate the complete system
print("=== Student Management System Demo ===")
# Create the system
sms = StudentManagementSystem()
# Add students
sms.add_student("S001", "Alice Johnson", 11, "11A")
sms.add_student("S002", "Bob Smith", 11, "11A")
sms.add_student("S003", "Charlie Brown", 12, "12B")
# Update grades (Math=0, Science=1, English=2, History=3)
sms.update_grade("S001", 0, 92) # Alice's Math
sms.update_grade("S001", 1, 88) # Alice's Science
sms.update_grade("S002", 0, 87) # Bob's Math
sms.update_grade("S002", 2, 91) # Bob's English
# Generate report cards
sms.generate_report_card("S001")
sms.generate_report_card("S002")
# Display class structure
sms.display_class_structure()
# Save to CSV
sms.save_to_csv("school_data.csv")
# Demonstrate undo
print("\n=== Undo Demo ===")
sms.undo_last_operation()
sms.generate_report_card("S002")
Summary¶
This section has shown you how to implement all the required data structures in Python:
-
Single/Multidimensional Arrays: Python lists for storing and processing collections
-
Lists: Full range of list operations for dynamic data management
-
Trees: Nested dictionaries and lists for hierarchical data
-
Stacks: List-based LIFO operations for undo systems and parsing
-
Hash Tables: Python dictionaries for fast lookup and data relationships
-
Sequential Files: CSV operations for data persistence and exchange
Key takeaways:
-
Choose the right data structure for your problem
-
Combine multiple structures for complex applications
-
Use proper algorithms for searching and sorting
-
Handle edge cases and error conditions
-
Test your implementations thoroughly