Introduction to Python | Data Structures Lecture Notes 2025–2026
INTRODUCTION TO PYTHON
Lecture Notes
Python Data Structures
Lists • Tuples • Dictionaries • Sets • Strings as Sequences • Comprehensions • Choosing
the Right Structure
Topic Covered In This Document
1. Introduction to Data Structures Overview, mutability, memory
2. Lists Create, index, slice, modify, methods, comprehensions
3. Tuples Immutability, packing/unpacking, use cases
4. Dictionaries Key-value pairs, CRUD, iteration, comprehensions
5. Sets Unique values, set operations, frozensets
6. Strings as Sequences Indexing, slicing, methods, f-strings
7. Comprehensions List, dict, set comprehensions
8. Choosing a Data Structure Decision guide, comparison table
9. Practice Exercises Graded exercises with solutions
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
1. Introduction to Python Data Structures
What is a Data Structure?
A data structure is a way of organising and storing data in a program so that it can be accessed,
modified, and processed efficiently. In Python, data structures are built into the language — you
do not need to import anything to use the four core types.
1.1 The Four Core Python Data Structures
Structure Syntax Ordered Mutable Duplicates Key Feature
List [1, 2, 3] Yes Yes ✓ Yes General-purpose
ordered collection
Tuple (1, 2, 3) Yes No ✗ Yes Fixed / immutable
sequence
Dictionary {'a': 1} Yes* Yes ✓ Keys: No Key-value lookup
Set {1, 2, 3} No Yes ✓ No Unique values, fast
membership test
* Dictionaries maintain insertion order from Python 3.7 onwards.
1.2 Mutability
Mutability determines whether you can change a data structure after it is created.
• Mutable structures (List, Dict, Set) can be changed in place — you can add, remove, or
modify elements.
• Immutable structures (Tuple, Frozenset, String) cannot be changed once created. Any
'modification' creates a new object.
⚑ Why Mutability Matters
Mutable objects are passed by reference — if you pass a list to a function and modify it
inside, the original list changes. Immutable objects are safe to pass around without fear of
accidental modification. This matters a great deal in data science when sharing datasets
between functions.
1.3 Memory and Performance Overview
Operation List Tuple Dictionary Set
Access by index O(1) O(1) N/A N/A
Access by key N/A N/A O(1) N/A
Membership test O(n) O(n) O(1) O(1)
Append/Add O(1) N/A O(1) O(1)
Insert at i O(n) N/A N/A N/A
Delete O(n) N/A O(1) O(1)
O(1) = constant time (fast). O(n) = linear time (slows as size grows).
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
2. Lists
A list is an ordered, mutable sequence of elements. It is the most versatile and commonly used
data structure in Python. Lists can hold elements of any type — even mixed types — and can
grow or shrink dynamically.
2.1 Creating Lists
# Empty list
empty = []
# Homogeneous lists
scores = [85, 92, 78, 95, 88]
names = ['Alice', 'Bob', 'Carol', 'Dave']
prices = [19.99, 34.50, 5.99]
# Mixed-type list (Python allows this)
record = ['Alice', 30, True, 95.5, None]
# Nested list (2-D matrix)
matrix = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
# Create from other sequences
from_range = list(range(5)) # [0, 1, 2, 3, 4]
from_tuple = list((1, 2, 3)) # [1, 2, 3]
from_string = list('hello') # ['h','e','l','l','o']
2.2 Indexing and Slicing
Python uses zero-based indexing. Negative indices count from the end.
fruits = ['apple', 'banana', 'cherry', 'date', 'elderberry']
# Positive indexing
print(fruits[0]) # 'apple' — first element
print(fruits[2]) # 'cherry'
print(fruits[-1]) # 'elderberry' — last element
print(fruits[-2]) # 'date' — second from last
# Slicing [start : stop : step] stop is EXCLUSIVE
print(fruits[1:3]) # ['banana', 'cherry']
print(fruits[:2]) # ['apple', 'banana'] — from start
print(fruits[2:]) # ['cherry', 'date', 'elderberry'] — to end
print(fruits[::2]) # ['apple', 'cherry', 'elderberry'] — every 2nd
print(fruits[::-1]) # reversed list
# Nested list access
matrix = [[1,2,3],[4,5,6],[7,8,9]]
print(matrix[1][2]) # 6 (row 1, column 2)
2.3 Modifying Lists — Mutability in Action
fruits = ['apple', 'banana', 'cherry']
# Change an element
fruits[0] = 'avocado'
print(fruits) # ['avocado', 'banana', 'cherry']
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
# append() — add ONE element to the end
[Link]('date')
# insert(index, value) — insert at a specific position
[Link](1, 'blueberry') # insert at index 1
# extend() — add all elements from another iterable
[Link](['fig', 'grape'])
# remove() — remove FIRST occurrence of a value
[Link]('banana')
# pop(index) — remove AND return element at index
last = [Link]() # removes last element
third = [Link](2) # removes element at index 2
# del — delete by index or slice
del fruits[0]
del fruits[1:3]
# clear() — remove all elements
[Link]() # fruits is now []
2.4 Searching and Sorting
nums = [5, 2, 8, 1, 9, 3, 2]
# index() — position of first occurrence
print([Link](2)) # 1
# count() — how many times a value appears
print([Link](2)) # 2
# sort() — sort IN PLACE (modifies the list)
[Link]()
print(nums) # [1, 2, 2, 3, 5, 8, 9]
[Link](reverse=True) # descending
# sorted() — returns a NEW sorted list, original unchanged
original = [5, 2, 8, 1]
new_list = sorted(original) # [1, 2, 5, 8]
print(original) # [5, 2, 8, 1] — unchanged
# Sort by custom key using lambda
students = [('Bob', 85), ('Alice', 92), ('Carol', 78)]
[Link](key=lambda s: s[1], reverse=True)
# [('Alice', 92), ('Bob', 85), ('Carol', 78)]
# Aggregates
print(min(nums), max(nums), sum(nums), len(nums))
2.5 List Comprehensions
List comprehensions are a concise, Pythonic way to build new lists. They replace many for-loop
patterns with a single readable line.
Syntax: [ expression for item in iterable if condition ]
# Basic: squares of 0–9
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
squares = [x**2 for x in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# With condition: only even squares
even_sq = [x**2 for x in range(10) if x % 2 == 0]
# [0, 4, 16, 36, 64]
# Transform strings
names = ['alice', 'bob', 'carol']
upper = [[Link]() for name in names]
# ['ALICE', 'BOB', 'CAROL']
# Filter: scores above 50
scores = [85, 42, 91, 67, 38, 95]
passing = [s for s in scores if s >= 50]
# [85, 91, 67, 95]
# Ternary inside comprehension
grades = ['Pass' if s >= 50 else 'Fail' for s in scores]
# ['Pass', 'Fail', 'Pass', 'Pass', 'Fail', 'Pass']
# Flatten a 2-D matrix into 1-D
matrix = [[1,2,3],[4,5,6],[7,8,9]]
flat = [val for row in matrix for val in row]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
2.6 Common List Methods — Quick Reference
Method Description Returns Example
append(x) Add x to end None [Link](5)
insert(i, x) Insert x at index i None [Link](0, 'a')
extend(iter) Add all items from iterable None [Link]([1,2])
remove(x) Remove first occurrence None [Link](3)
of x
pop(i) Remove & return item at item [Link]()
index i
index(x) Index of first occurrence of int [Link](7)
x
count(x) Count occurrences of x int [Link](2)
sort() Sort in place None [Link]()
reverse() Reverse in place None [Link]()
copy() Shallow copy list lst2 = [Link]()
clear() Remove all items None [Link]()
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
3. Tuples
A tuple is an ordered, immutable sequence. Once created, its elements cannot be added,
removed, or changed. Tuples are faster and use less memory than lists, and can be used as
dictionary keys.
3.1 Creating Tuples
# Empty tuple
t0 = ()
# Single-element tuple — the COMMA is essential
t1 = (42,) # This IS a tuple
t2 = (42) # This is just the integer 42 — NOT a tuple!
# Multi-element tuples
coords = (10.5, 20.3) # latitude, longitude
person = ('Alice', 30, 'F') # name, age, gender
rgb = (255, 128, 0) # red, green, blue
# Tuple packing — parentheses are optional
point = 3, 4 # same as (3, 4)
# Convert from other sequences
from_list = tuple([1, 2, 3]) # (1, 2, 3)
from_str = tuple('abc') # ('a', 'b', 'c')
3.2 Indexing and Slicing
Tuples use the same zero-based indexing and slicing syntax as lists.
t = (10, 20, 30, 40, 50)
print(t[0]) # 10 — first element
print(t[-1]) # 50 — last element
print(t[1:4]) # (20, 30, 40)
print(t[::-1]) # (50, 40, 30, 20, 10) — reversed
# Nested tuple access
matrix = ((1,2), (3,4), (5,6))
print(matrix[1][0]) # 3
3.3 Tuple Unpacking
Tuple unpacking assigns elements to individual variables in one line — one of Python's most
elegant features.
# Basic unpacking
coords = (48.8, 2.35) # Paris: lat, lon
lat, lon = coords
print(lat) # 48.8
print(lon) # 2.35
# Swap two variables without a temporary variable
a, b = 10, 20
a, b = b, a
print(a, b) # 20 10
# Extended unpacking with *
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
first, *rest = (1, 2, 3, 4, 5)
print(first) # 1
print(rest) # [2, 3, 4, 5]
*start, last = (1, 2, 3, 4, 5)
print(last) # 5
# Unpacking in a for loop — very common pattern
students = [('Alice', 90), ('Bob', 85), ('Carol', 92)]
for name, score in students:
print(f'{name}: {score}')
# Alice: 90 Bob: 85 Carol: 92
3.4 When to Use Tuples vs Lists
Criterion Tuple List
Mutability Immutable — cannot change Mutable — can change
Syntax Parentheses () or commas Square brackets []
Performance Slightly faster, less memory Slightly slower, more memory
As dict key Yes — tuples are hashable No — lists are not hashable
Typical use Fixed records, coordinates, RGB Collections that grow/shrink
Intent signal Data will NOT change Data may change
⚑ Lecturer Tip — Tuples Signal Intent
When you use a tuple, you are communicating to other programmers: 'this data is fixed and
should not change.' Use tuples for things like GPS coordinates, database row records, RGB
colour values, and function return values where order is significant.
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
4. Dictionaries
A dictionary (dict) stores data as key-value pairs. Each key maps to exactly one value. Keys
must be unique and immutable. Dictionaries are the closest Python equivalent to a JSON object, a
database record, or a hash map.
4.1 Creating Dictionaries
# Empty dict
d = {} # or: d = dict()
# Dictionary literal
student = {'name': 'Alice', 'age': 22, 'gpa': 3.8}
# Mixed value types
record = {
'id': 12345,
'name': 'Alice Doe',
'scores': [85, 92, 78], # list as a value
'active': True,
'email': None
}
# From keyword arguments
d2 = dict(name='Bob', age=25, city='Nairobi')
# From a list of (key, value) pairs
pairs = [('a', 1), ('b', 2), ('c', 3)]
d3 = dict(pairs) # {'a': 1, 'b': 2, 'c': 3}
4.2 Accessing and Modifying Values
student = {'name': 'Alice', 'age': 22, 'gpa': 3.8}
# READ — access by key
print(student['name']) # 'Alice'
# student['email'] → KeyError if key doesn't exist
# Safe access with get() — returns None or a default
print([Link]('email')) # None
print([Link]('email', 'N/A')) # 'N/A'
# CREATE / UPDATE
student['email'] = 'alice@[Link]' # add new key
student['age'] = 23 # update existing key
# Update multiple keys at once
[Link]({'age': 24, 'gpa': 3.9})
# DELETE
del student['email']
gpa = [Link]('gpa') # removes and returns the value
# Check key existence
print('name' in student) # True
print('gpa' in student) # False (we just removed it)
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
4.3 Iterating over Dictionaries
grades = {'Alice': 90, 'Bob': 85, 'Carol': 92, 'Dave': 78}
# Iterate over KEYS (default)
for name in grades: # same as [Link]()
print(name)
# Iterate over VALUES
for score in [Link]():
print(score)
average = sum([Link]()) / len(grades) # 86.25
# Iterate over KEY-VALUE PAIRS — most common
for name, score in [Link]():
print(f'{name}: {score}')
# setdefault() — set only if key is absent
[Link]('Eve', 0) # adds Eve:0 only if missing
# Merge two dicts (Python 3.9+)
extra = {'Frank': 88}
merged = grades | extra
4.4 Nested Dictionaries
Dictionaries can contain other dictionaries, creating hierarchical data — the same structure as
JSON (the standard format for REST API responses).
students = {
'STU001': {
'name': 'Alice Doe',
'grades': {'Python': 88, 'ML': 92},
'active': True
},
'STU002': {
'name': 'Bob Smith',
'grades': {'Python': 75, 'Stats': 83},
'active': False
}
}
# Access nested values
print(students['STU001']['name']) # 'Alice Doe'
print(students['STU001']['grades']['ML']) # 92
# Safe nested access using chained .get()
ml = [Link]('STU001', {}).get('grades', {}).get('ML', 'N/A')
# Iterate over all students
for sid, info in [Link]():
print(f"{sid}: {info['name']} — Active: {info['active']}")
4.5 Dictionary Comprehensions
Syntax: { key_expr : value_expr for item in iterable if condition }
# Map each number to its square
squares = {x: x**2 for x in range(6)}
# {0:0, 1:1, 2:4, 3:9, 4:16, 5:25}
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
# Invert a dictionary (swap keys and values)
original = {'a': 1, 'b': 2, 'c': 3}
inverted = {v: k for k, v in [Link]()}
# {1: 'a', 2: 'b', 3: 'c'}
# Filter: keep only passing students
grades = {'Alice': 90, 'Bob': 45, 'Carol': 82, 'Dave': 35}
passing = {name: s for name, s in [Link]() if s >= 50}
# {'Alice': 90, 'Carol': 82}
# Normalise scores to 0–1 scale
top = max([Link]()) # 90
norm = {n: round(s/top, 3) for n, s in [Link]()}
# {'Alice': 1.0, 'Bob': 0.5, 'Carol': 0.911, 'Dave': 0.389}
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
5. Sets
A set is an unordered collection of unique elements. Sets are mutable (you can add/remove
items) but do not support indexing, since they have no order. Sets are ideal for removing
duplicates and performing mathematical set operations.
5.1 Creating Sets
# Set literal
fruits = {'apple', 'banana', 'cherry'}
# IMPORTANT: {} creates an empty DICT, not a set!
empty_dict = {} # This is a dict
empty_set = set() # This is a set
# From a list — duplicates are removed automatically
nums = set([1, 2, 2, 3, 3, 3, 4])
print(nums) # {1, 2, 3, 4} — no duplicates
# Common use: remove duplicates from a list
tags = ['python', 'data', 'python', 'ml', 'data', 'python']
unique_tags = list(set(tags))
print(unique_tags) # ['data', 'ml', 'python'] (order may vary)
5.2 Adding and Removing Elements
s = {1, 2, 3}
# add() — add a single element
[Link](4)
print(s) # {1, 2, 3, 4}
# Adding a duplicate does nothing
[Link](2) # {1, 2, 3, 4} — unchanged
# update() — add multiple elements
[Link]([5, 6, 7])
# remove() — raises KeyError if not found
[Link](3)
# discard() — does NOT raise error if not found (safer)
[Link](99) # no error
# pop() — remove and return an ARBITRARY element
x = [Link]()
# clear() — empty the set
[Link]()
5.3 Set Operations
Sets support the four fundamental mathematical operations.
A = {1, 2, 3, 4, 5}
B = {4, 5, 6, 7, 8}
# Union — all elements from both sets
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
print(A | B) # {1, 2, 3, 4, 5, 6, 7, 8}
print([Link](B)) # same result
# Intersection — elements in BOTH sets
print(A & B) # {4, 5}
print([Link](B))
# Difference — in A but NOT in B
print(A - B) # {1, 2, 3}
print([Link](B))
# Symmetric difference — in one but NOT both
print(A ^ B) # {1, 2, 3, 6, 7, 8}
print(A.symmetric_difference(B))
# Subset and superset
C = {1, 2}
print([Link](A)) # True — all of C is in A
print([Link](C)) # True — A contains all of C
# Membership test — O(1) — much faster than lists for large data
print(3 in A) # True
print(9 in A) # False
5.4 Frozenset — Immutable Sets
A frozenset is an immutable version of a set. Because it is hashable, it can be used as a
dictionary key or stored inside another set.
fs = frozenset([1, 2, 3, 4])
# [Link](5) → AttributeError: 'frozenset' has no 'add'
# Use as a dict key
d = {frozenset({1, 2}): 'pair', frozenset({3, 4}): 'other pair'}
⚑ Practical Use of Sets in Data Science
Sets are commonly used to: (1) find unique values in a column quickly, (2) compute overlap
between two lists (e.g., customers who bought both products), (3) fast membership testing
when checking millions of records. Membership testing with a set is O(1); with a list it is O(n)
— a massive difference at scale.
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
6. Strings as Sequences
Strings are immutable sequences of characters. They support the same indexing and slicing
operations as lists and tuples, and have a rich set of built-in methods for text processing — a
crucial skill in data science.
6.1 Indexing and Slicing Strings
s = 'Python'
print(s[0]) # 'P' — first character
print(s[-1]) # 'n' — last character
print(s[0:3]) # 'Pyt' — slice [start:stop]
print(s[2:]) # 'thon'
print(s[::-1]) # 'nohtyP' — reversed
# Strings are immutable — this raises a TypeError:
# s[0] = 'J' → TypeError: 'str' does not support item assignment
6.2 Essential String Methods
text = ' Hello, Data Science! '
# Cleaning
print([Link]()) # remove leading/trailing whitespace
print([Link]()) # remove leading whitespace only
print([Link]()) # remove trailing whitespace only
# Case
print([Link]()) # 'hello, data science!'
print([Link]()) # 'HELLO, DATA SCIENCE!'
print([Link]()) # 'Hello, Data Science!'
# Search
print([Link]('Data')) # 9 (index of first occurrence)
print([Link]('e')) # 2
print([Link](' Hel')) # True
print([Link]('! ')) # True
# Modification
print([Link]('Hello', 'Hi'))
print([Link]().split(', ')) # ['Hello', 'Data Science!']
# Joining
words = ['Python', 'is', 'great']
print(' '.join(words)) # 'Python is great'
print('-'.join(words)) # 'Python-is-great'
# Checking content
print('123'.isdigit()) # True
print('abc'.isalpha()) # True
print('abc123'.isalnum()) # True
print(' '.isspace()) # True
6.3 f-Strings (Formatted String Literals)
f-strings (Python 3.6+) are the preferred way to embed expressions inside strings.
name = 'Alice'
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
score = 95.678
passed = True
# Basic embedding
print(f'Student: {name}')
# Student: Alice
# Format specifiers
print(f'Score: {score:.2f}') # Score: 95.68 (2 decimal places)
print(f'Score: {score:.0f}') # Score: 96 (0 decimal places)
print(f'Score: {score:8.2f}') # Score: 95.68 (width 8, right-aligned)
print(f'Score: {score:<8.2f}') # Score: 95.68 (left-aligned)
# Expressions inside f-strings
print(f'Grade: {"Pass" if passed else "Fail"}')
print(f'Double: {score * 2:.1f}')
# Dictionary values
student = {'name': 'Bob', 'gpa': 3.7}
print(f"Name: {student['name']}, GPA: {student['gpa']}")
6.4 String Methods Quick Reference
Method Description Example Result
strip() Remove whitespace ' hi '.strip() 'hi'
split(sep) Split into list 'a,b'.split(',') ['a','b']
join(iter) Join with separator '-'.join(['a','b']) 'a-b'
replace(a,b) Replace substring 'hi'.replace('i','ello') 'hello'
find(s) Index of substring 'hello'.find('ll') 2
upper() All uppercase 'hi'.upper() 'HI'
lower() All lowercase 'HI'.lower() 'hi'
startswith(s) Check prefix 'Python'.startswith('Py') True
endswith(s) Check suffix '[Link]'.endswith('.py') True
count(s) Count occurrences 'aabaa'.count('a') 4
strip/lstrip/rstrip Remove chars '###hi###'.strip('#') 'hi'
isdigit() All digits? '123'.isdigit() True
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
7. Comprehensions
Python supports comprehension syntax for lists, dictionaries, and sets. Comprehensions are
faster than equivalent for-loop constructs and are central to idiomatic, professional Python.
7.1 List Comprehensions (covered in depth in Section 2.5)
Syntax: [ expression for item in iterable if condition ]
# Even numbers 0–20
evens = [x for x in range(21) if x % 2 == 0]
# Celsius to Fahrenheit
celsius = [0, 10, 20, 30, 40]
fahrenheit = [(c * 9/5) + 32 for c in celsius]
# [32.0, 50.0, 68.0, 86.0, 104.0]
7.2 Dictionary Comprehensions (covered in Section 4.5)
Syntax: { key : value for item in iterable if condition }
7.3 Set Comprehensions
Syntax: { expression for item in iterable if condition }
# Unique lengths of words
words = ['cat', 'dog', 'elephant', 'cat', 'ant', 'dog']
lengths = {len(w) for w in words}
print(lengths) # {3, 8} (order may vary)
# First letters of names
names = ['Alice', 'Bob', 'Anna', 'Carol', 'Ben']
initials = {name[0] for name in names}
print(initials) # {'A', 'B', 'C'}
7.4 Generator Expressions
Generator expressions look like list comprehensions but use parentheses (). They produce items
lazily — one at a time — using almost no memory. Use them when you only need to iterate once
over large data.
# List comprehension — stores all 1 million values in memory
squares_list = [x**2 for x in range(1_000_000)]
# Generator expression — generates values on demand
squares_gen = (x**2 for x in range(1_000_000))
# Consume with next() or a for loop
print(next(squares_gen)) # 0
print(next(squares_gen)) # 1
# sum() accepts a generator — very memory-efficient
total = sum(x**2 for x in range(1_000_000))
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
7.5 Comparison: For Loop vs Comprehension
Approach Code Memory Speed Readability
for loop squares = [] for x in range(10): O(n) Slower Verbose
[Link](x**2)
Comprehension [x**2 for x in range(10)] O(n) Faster Concise
Generator (x**2 for x in range(10)) O(1) Fastest Concise
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
8. Choosing the Right Data Structure
Choosing the correct data structure is a fundamental programming skill. The wrong choice leads
to slow code, unnecessary complexity, and bugs. Use this section as a decision guide.
8.1 Decision Guide
If you need to... Use Why
Store an ordered collection that List Mutable, indexed, flexible
changes
Store fixed records (coordinates, Tuple Immutable, faster, signals intent
colours)
Look up values by a meaningful Dictionary O(1) key access
name/key
Store unique items with no Set Automatic deduplication
duplicates
Perform fast membership testing Set or Dict O(1) vs O(n) for list
Use a collection as a dict key Tuple or Frozenset Must be hashable
Process text / character sequences String Rich text methods
Work with large data one item at a Generator O(1) memory usage
time
8.2 Full Comparison Table
Feature List Tuple Dict Set String
Ordered Yes Yes Yes* No Yes
Mutable Yes No Yes Yes No
Duplicate values Yes Yes Values No Yes
Index access Yes Yes No No Yes
Key access No No Yes No No
Hashable (dict key) No Yes No No Yes
Membership O(1) No O(n) No O(n) Yes Yes No O(n)
Comprehension Yes No Yes Yes No
Typical use General Fixed Lookup Unique Text data
collections records tables items
* Dicts maintain insertion order from Python 3.7+
8.3 Common Mistakes to Avoid
Mistake Problem Fix
Using a list when Duplicates silently enter Use a set
uniqueness is needed
Using == to compare with Unreliable for NoneType Use 'is None'
None
Modifying a list while Skips elements Iterate over a copy: list(lst)
iterating over it unexpectedly
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
Forgetting the comma in a (42) is an int, not a tuple Write (42,)
single-element tuple
Direct key access without KeyError if key missing Use [Link](key, default)
.get()
Using {} for an empty set {} creates a dict, not a set Use set()
Comparing floats with == 0.1+0.2 != 0.3 due to Use abs(a-b) < 1e-9
IEEE 754
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
9. Practice Exercises
Work through these exercises in order. Exercises are graded: ★ = beginner, ★★ = intermediate,
★★★ = advanced.
9.1 Lists ★
Exercise 1
Given the list: temps = [22, 18, 25, 30, 15, 28, 20]
1. Sort the list in descending order.
2. Find the highest and lowest temperature using built-in functions.
3. Using a list comprehension, create a new list that contains only temperatures above 20.
4. Using a list comprehension, label each temperature 'Hot' if above 25, 'Warm' if 20–25,
'Cool' if below 20.
Exercise 2
Write a function flatten(matrix) that accepts a 2-D list (list of lists) and returns a single flat list
using a list comprehension.
9.2 Tuples ★
Exercise 3
You have a list of student records: records = [('Alice', 22, 88), ('Bob', 19, 75),
('Carol', 21, 92)]
5. Use a for loop with tuple unpacking to print each student in the format: Alice (age 22): 88
marks
6. Sort the records by score in descending order using sorted() with a lambda key.
7. Explain why you would use a tuple rather than a list for each student record.
9.3 Dictionaries ★★
Exercise 4
Given: grades = {'Alice': 90, 'Bob': 45, 'Carol': 82, 'Dave': 35, 'Eve': 71}
8. Using a dictionary comprehension, create a new dictionary containing only students who
passed (score >= 50).
9. Using a dictionary comprehension, normalise all scores to a 0–100 scale where the
highest score becomes 100.
10. Add a new student 'Frank' with a score of 68 without using update().
11. Safely access the score of a student 'Grace' who is not in the dictionary, returning 0 as
default.
Exercise 5 ★★
Write a function word_frequency(text) that accepts a string and returns a dictionary where
each key is a word and the value is how many times that word appears. Use .split() and a dict
comprehension or loop. Test with: 'the cat sat on the mat the cat'
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
9.4 Sets ★★
Exercise 6
You have two lists of students who enrolled in different courses:
python_students = ['Alice', 'Bob', 'Carol', 'Dave', 'Eve']
ml_students = ['Carol', 'Dave', 'Frank', 'Grace', 'Alice']
12. Find all students enrolled in BOTH courses.
13. Find students enrolled in Python but NOT ML.
14. Find all unique students across both courses.
15. Find students enrolled in one course but NOT both.
9.5 Mixed ★★★
Exercise 7 — Student Database
Build a student database using nested dictionaries. Write functions to:
16. add_student(db, student_id, name, scores_dict) — add a new student.
17. get_average(db, student_id) — return the student's average score across all subjects.
18. top_student(db) — return the name of the student with the highest average score.
19. passing_students(db, threshold=50) — return a list of names of students whose average is
above the threshold.
Test your database with at least 4 students, each with scores in 3 subjects.
9.6 Solutions — Selected
Exercise 1 — Solution
temps = [22, 18, 25, 30, 15, 28, 20]
# 1. Sort descending
sorted_temps = sorted(temps, reverse=True)
print(sorted_temps) # [30, 28, 25, 22, 20, 18, 15]
# 2. Max and min
print(max(temps), min(temps)) # 30 15
# 3. Only above 20
above_20 = [t for t in temps if t > 20]
# [22, 25, 30, 28]
# 4. Labels
labels = ['Hot' if t > 25 else 'Warm' if t >= 20 else 'Cool' for t in temps]
# ['Warm', 'Cool', 'Warm', 'Hot', 'Cool', 'Hot', 'Warm']
Exercise 5 — Word Frequency Solution
def word_frequency(text):
'''Return a dict of word: count pairs for each word in text.'''
words = [Link]().split()
return {word: [Link](word) for word in set(words)}
print(word_frequency('the cat sat on the mat the cat'))
# {'on': 1, 'the': 3, 'mat': 1, 'sat': 1, 'cat': 2}
Exercise 6 — Sets Solution
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
python_students = {'Alice', 'Bob', 'Carol', 'Dave', 'Eve'}
ml_students = {'Carol', 'Dave', 'Frank', 'Grace', 'Alice'}
# 1. Both courses
print(python_students & ml_students) # {'Alice', 'Carol', 'Dave'}
# 2. Python only
print(python_students - ml_students) # {'Bob', 'Eve'}
# 3. All unique students
print(python_students | ml_students) # all 7 students
# 4. Exactly one course (not both)
print(python_students ^ ml_students) # {'Bob', 'Eve', 'Frank', 'Grace'}
Python Data Structures — For Classroom Use Only
Introduction to Python | Data Structures Lecture Notes 2025–2026
10. Summary and Quick Reference
10.1 Data Structure Comparison
Structure Syntax Ordered Mutable Duplicates Hashable Best For
List [1,2,3] Yes Yes ✓ Yes No Ordered, changeable
collections
Tuple (1,2,3) Yes No ✗ Yes Yes Fixed records, dict keys
Dictionary {'a':1} Yes* Yes ✓ No No Key-value lookup
Set {1,2,3} No Yes ✓ No No Unique items, set
operations
Frozenset frozenset() No No ✗ No Yes Immutable unique items
String 'abc' Yes No ✗ Yes Yes Text processing
10.2 Best Practices
• Use descriptive variable names: student_scores, not ss or x.
• Prefer tuples for fixed data — it signals to readers that the data should not change.
• Use .get() on dicts to avoid KeyError when a key may not exist.
• Use sets for deduplication and membership testing over large datasets.
• Use list/dict/set comprehensions over for loops for cleaner, faster code.
• Never use {} for an empty set — always use set().
• Never compare floats with == — use abs(a - b) < 1e-9 instead.
• Use f-strings for all string formatting — they are readable and fast.
• Restart and Run All in Jupyter before submitting work — ensure cells run in order.
10.3 Further Reading
• Official Python Docs: [Link]/3/tutorial/[Link]
• Real Python — Python Data Structures: [Link]
• PEP 8 — Python Style Guide: [Link]/pep-0008/
— End of Lecture Notes —
Python Data Structures — For Classroom Use Only