Python Proficiency for Developers
Advanced Data Structures
Python’s Built-in Data Organizers
Most programming involves juggling data. But you can't just leave it scattered around. You need containers to hold it, organize it, and access it efficiently. Python comes with four powerful, all-purpose data structures built right in: lists, tuples, sets, and dictionaries. Each has its own strengths and is suited for different tasks.
The Core Four
Let's look at the fundamental tools for storing collections of data.
Lists: Ordered and Changeable
Think of a list as a shopping list. It's an ordered sequence of items, and you can change it whenever you want. You can add items, remove them, or rearrange their order. This flexibility makes lists one of the most common data structures you'll use.
# A list of tasks
tasks = ['laundry', 'dishes', 'walk the dog']
# Add an item to the end
tasks.append('buy groceries')
print(tasks) # -> ['laundry', 'dishes', 'walk the dog', 'buy groceries']
# Remove an item
tasks.remove('dishes')
print(tasks) # -> ['laundry', 'walk the dog', 'buy groceries']
# Access an item by its position (index)
first_task = tasks[0]
print(first_task) # -> 'laundry'
Tuples: Ordered and Unchangeable
A tuple is like a list, but with one key difference: it's immutable. Once you create a tuple, you can't change it. This might seem limiting, but it's useful for data that should remain constant, like geographic coordinates or RGB color values. Their unchangeable nature also makes them slightly more memory-efficient than lists.
# A tuple representing a point in 2D space
point = (10, 20)
# You can access elements by index
print(point[0]) # -> 10
# But you can't change them!
# The following line would cause an error:
# point[0] = 15 # -> TypeError: 'tuple' object does not support item assignment
Sets: Unordered and Unique
A set is a collection where every item must be unique. Order doesn't matter. Think of it as a bag of unique items. If you try to add something that's already in the set, nothing happens. Sets are incredibly fast for checking if an item is present in a collection.
# A set of unique tags for a blog post
tags = {'python', 'data', 'code', 'programming'}
# Adding a duplicate element does nothing
tags.add('python')
print(tags) # -> {'python', 'data', 'code', 'programming'}
# Check for membership quickly
'data' in tags # -> True
'java' in tags # -> False
Dictionaries: Key-Value Pairs
A dictionary stores data in key-value pairs, much like a real-world dictionary connects a word (the key) to its definition (the value). Keys must be unique. Dictionaries are optimized for retrieving a value when you know its corresponding key.
# A dictionary storing a user's information
user_profile = {
'username': 'alex',
'email': 'alex@example.com',
'member_since': 2021
}
# Access a value using its key
print(user_profile['email']) # -> 'alex@example.com'
# Add a new key-value pair
user_profile['plan'] = 'premium'
print(user_profile['plan']) # -> 'premium'
Specialized Tools from `collections`
Sometimes the basic data structures aren't quite the right fit. For those situations, Python's collections module offers more specialized containers that can make your code simpler and more efficient.
Deque: The Double-Ended Queue
A
deque(pronounced 'deck') is a 'double-ended queue.' While lists are fast at adding and removing items from the end (appendandpop), they are slow at adding or removing from the beginning because all other elements need to be shifted. Adequeis designed to be fast at adding and removing elements from both ends.
from collections import deque
# A queue of people waiting in line
line = deque(['Bob', 'Jane', 'Sue'])
# A new person arrives at the back
line.append('Tom')
print(line) # -> deque(['Bob', 'Jane', 'Sue', 'Tom'])
# The first person is served
line.popleft()
print(line) # -> deque(['Jane', 'Sue', 'Tom'])
# Someone cuts in at the front
line.appendleft('Alice')
print(line) # -> deque(['Alice', 'Jane', 'Sue', 'Tom'])
Counter: For Tallying Items
A Counter is a dictionary subclass designed for counting things. You can give it a sequence of items, and it will return a dictionary-like object where keys are the items and values are their counts.
from collections import Counter
votes = ['red', 'blue', 'red', 'green', 'blue', 'red']
# Tally the votes
vote_counts = Counter(votes)
print(vote_counts)
# -> Counter({'red': 3, 'blue': 2, 'green': 1})
# How many votes for 'red'?
print(vote_counts['red']) # -> 3
# Find the most common item
print(vote_counts.most_common(1)) # -> [('red', 3)]
Defaultdict: Dictionaries with a Default
Have you ever tried to access a dictionary key that doesn't exist? You get a KeyError. A defaultdict solves this. When you create one, you provide a function (like int, list, or set) that will be used to generate a default value for any key that hasn't been set yet. This is perfect for grouping or counting items.
from collections import defaultdict
# Group students by their house. The default value will be an empty list.
students_by_house = defaultdict(list)
students = [
('Harry', 'Gryffindor'),
('Draco', 'Slytherin'),
('Luna', 'Ravenclaw'),
('Hermione', 'Gryffindor')
]
for name, house in students:
# No need to check if the key exists first!
# If 'house' is a new key, it's created with an empty list.
students_by_house[house].append(name)
print(students_by_house['Gryffindor']) # -> ['Harry', 'Hermione']
print(students_by_house['Slytherin']) # -> ['Draco']
print(students_by_house['Hufflepuff']) # -> [] (key is created with a default empty list)
Choosing the right data structure is a key part of writing clean, efficient code. By understanding these fundamental and specialized tools, you can handle a wide variety of programming challenges more effectively.
Time to check your understanding.
Which of the following data structures is immutable, meaning its contents cannot be changed after creation?
You have a list of words from a document and you need to find the frequency of each word. Which specialized container from the collections module is best suited for this task?