BrightPath
Back to Course
Year 12 Coding

Python Data Structures

Explore tuples, sets, deques, and learn how to choose the right data structure for each problem.

Tuples: Immutable Sequences

A tuple is an ordered, immutable sequence. Once created, its elements cannot be changed, added, or removed. Tuples are ideal for fixed collections like coordinates, RGB colours, or database records.

# Creating tuples
coordinates = (3, 7)
rgb_red = (255, 0, 0)
single = (42,)  # comma needed for single-element tuple

# Tuple unpacking
x, y = coordinates
print(f"x={x}, y={y}")  # x=3, y=7

# Named tuples for readable code
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(10, 20)
print(p.x, p.y)  # 10 20

# Tuples as dictionary keys (lists cannot be)
locations = {(0, 0): "Origin", (1, 2): "Point A"}
print(locations[(0, 0)])  # Origin

Tuples use less memory than lists and are faster to iterate. Use them when data should not change.

Sets: Unique Collections

A set is an unordered collection of unique elements. Sets excel at membership testing, removing duplicates, and performing mathematical set operations (union, intersection, difference).

# Creating and using sets
fruits = {"apple", "banana", "cherry", "apple"}
print(fruits)  # {'apple', 'banana', 'cherry'} - no duplicates

# Fast membership testing: O(1) average
print("banana" in fruits)  # True

# Set operations
a = {1, 2, 3, 4, 5}
b = {4, 5, 6, 7, 8}

print(a | b)    # Union:        {1, 2, 3, 4, 5, 6, 7, 8}
print(a & b)    # Intersection: {4, 5}
print(a - b)    # Difference:   {1, 2, 3}
print(a ^ b)    # Symmetric:    {1, 2, 3, 6, 7, 8}

# Remove duplicates from a list
names = ["Alice", "Bob", "Alice", "Charlie", "Bob"]
unique_names = list(set(names))
print(unique_names)  # ['Alice', 'Bob', 'Charlie']

Deques and Choosing the Right Structure

A deque (double-ended queue) from collections allows efficient append and pop from both ends. Lists are slow at inserting or removing from the front (O(n)), but deques do this in O(1).

from collections import deque

# Create a deque
queue = deque(["task1", "task2", "task3"])

# Efficient operations on both ends
queue.append("task4")       # add to right
queue.appendleft("task0")   # add to left
print(queue)  # deque(['task0', 'task1', 'task2', 'task3', 'task4'])

queue.pop()       # remove from right -> 'task4'
queue.popleft()   # remove from left  -> 'task0'

# Fixed-size deque (keeps last N items)
recent = deque(maxlen=3)
for i in range(5):
    recent.append(i)
print(recent)  # deque([2, 3, 4], maxlen=3)

When to Use Each Structure

Structure Best For Mutable?
listOrdered, changeable collectionsYes
tupleFixed data, dictionary keys, function returnsNo
setUnique items, fast membership checksYes
dictKey-value lookups, structured dataYes
dequeQueues, stacks, sliding windowsYes

Key Vocabulary

Immutable

An object whose state cannot be changed after creation. Tuples and strings are immutable.

Hash Table

The underlying data structure for sets and dicts, enabling O(1) average lookups.

Deque

A double-ended queue that supports efficient appends and pops from both ends.

Time Complexity

A measure of how an operation's speed scales with input size, expressed as Big-O notation (e.g., O(1), O(n)).

Worked Examples

1

Find common elements between two lists using sets

def find_common(list_a, list_b):
    """Return sorted list of elements in both lists."""
    set_a = set(list_a)
    set_b = set(list_b)
    return sorted(set_a & set_b)  # intersection

subjects_alice = ["Maths", "English", "Science", "Art"]
subjects_bob = ["Science", "Music", "Maths", "PE"]

common = find_common(subjects_alice, subjects_bob)
print(common)  # ['Maths', 'Science']

Why sets? Converting to sets makes the intersection O(min(n, m)) instead of O(n * m) with nested loops.

2

Implement a browser history using a deque

from collections import deque

class BrowserHistory:
    def __init__(self, max_history=50):
        self.history = deque(maxlen=max_history)
        self.current = None

    def visit(self, url):
        if self.current:
            self.history.append(self.current)
        self.current = url

    def back(self):
        if self.history:
            self.current = self.history.pop()
        return self.current

browser = BrowserHistory()
browser.visit("google.com")
browser.visit("github.com")
browser.visit("python.org")
print(browser.back())  # github.com
print(browser.back())  # google.com

maxlen ensures the history never exceeds 50 entries, automatically dropping the oldest when full.

3

Use named tuples for structured records

from collections import namedtuple

Student = namedtuple("Student", ["name", "year", "gpa"])

students = [
    Student("Alice", 12, 3.9),
    Student("Bob", 12, 3.5),
    Student("Charlie", 12, 3.8),
]

# Sort by GPA descending
top_students = sorted(students, key=lambda s: s.gpa, reverse=True)
for s in top_students:
    print(f"{s.name}: GPA {s.gpa}")
# Alice: GPA 3.9
# Charlie: GPA 3.8
# Bob: GPA 3.5

# Access by name (more readable than index)
print(top_students[0].name)  # Alice

Named tuples give you the immutability of tuples with the readability of named attributes. They are lighter than full classes.

Knowledge Check

Select the correct answer for each question. Click "Check Answer" to see if you are right.

Question 1

What happens when you try to modify an element in a tuple?

Question 2

What does {1, 2, 3} & {2, 3, 4} return?

Question 3

Why would you use a deque instead of a list for a queue?

Question 4

Which data structure would you choose to store unique student IDs for fast lookup?

Question 5

How do you create a single-element tuple in Python?

Key Concepts Summary

Previous: Inheritance Next: Algorithms