No history yet

Pythonic Data Structures

Beyond Basic Lists and Loops

You're already comfortable using lists to store items and loops to iterate over them. Now, let's explore more specialized and efficient ways to manage data in Python. These techniques, often called "Pythonic," help you write cleaner, more readable, and faster code.

Let's start with dictionaries. You know they're great for storing key-value pairs, but they have some handy methods for more complex situations. For instance, trying to access a key that doesn't exist will raise a KeyError. A safer way to retrieve a value is by using the .get() method. It lets you provide a default value to return if the key isn't found.

user_scores = {"alice": 88, "bob": 92}

# Safely get a score, defaulting to 0 if the user doesn't exist
charlie_score = user_scores.get("charlie", 0)
print(f"Charlie's score: {charlie_score}") # Output: Charlie's score: 0

# Iterating over keys and values is cleaner with .items()
for user, score in user_scores.items():
    print(f"{user} scored {score}")

Sets for Uniqueness and Logic

What if you need to store a collection of items where duplicates are irrelevant? For example, tracking which unique users have visited a webpage. A list would require extra code to check for duplicates before adding a new user. A set is the perfect tool for this job.

Sets are unordered collections of unique elements. They are highly optimized for membership testing (checking if an item is in the set) and removing duplicates from a sequence.

# Remove duplicates from a list
logins = ["alice", "bob", "charlie", "alice", "dave", "bob"]
unique_logins = set(logins)
print(unique_logins) # Output: {'charlie', 'bob', 'dave', 'alice'}

Beyond uniqueness, sets excel at performing mathematical set operations. Imagine you have two groups of users and want to find out who is in one group but not the other, or who is in both. Sets make these logical operations incredibly straightforward and efficient.

  • Intersection (&): Elements present in both sets.
  • Union (|): All elements from both sets combined.
  • Difference (-): Elements in the first set but not in the second.
  • Symmetric Difference (^): Elements in either set, but not both.
premium_users = {"alice", "bob", "eve"}
beta_testers = {"bob", "charlie", "dave"}

# Who is both a premium user and a beta tester?
print(premium_users & beta_testers) # Output: {'bob'}

# Who is a premium user but not a beta tester?
print(premium_users - beta_testers) # Output: {'alice', 'eve'}

# All unique users across both groups
print(premium_users | beta_testers) # Output: {'alice', 'bob', 'charlie', 'dave', 'eve'}

Concise Code with Comprehensions

A common pattern in programming is to create a new list by performing an operation on each item of an existing sequence. The standard way involves initializing an empty list and using a for loop with an .append() call. offer a more elegant and often faster way to do this in a single line.

Let's say we want to create a list of the squares of numbers from 0 to 9. Here's how you'd do it with a for loop versus a list comprehension.

# The standard for loop
squares = []
for i in range(10):
    squares.append(i * i)

# The list comprehension equivalent
squares_comp = [i * i for i in range(10)]

print(squares == squares_comp) # Output: True

The syntax is [expression for item in iterable]. You can also add a condition to filter the items.

# Create a list of even squares
even_squares = [i * i for i in range(10) if i % 2 == 0]
print(even_squares) # Output: [0, 4, 16, 36, 64]

This powerful syntax isn't limited to lists. You can use it to create sets and dictionaries, too. Set comprehensions use curly braces {} instead of square brackets [] and automatically handle uniqueness. Dictionary comprehensions also use curly braces but require a key-value pair in the expression.

# Set comprehension to get unique squares
numbers = [1, 2, 2, 3, 4, 4, 5]
unique_squares_set = {n**2 for n in numbers}
print(unique_squares_set) # Output: {1, 4, 9, 16, 25}

# Dictionary comprehension to map numbers to their squares
square_map = {x: x**2 for x in range(5)}
print(square_map) # Output: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

Specialized Collection Types

Python's built-in data structures are great, but sometimes you need a little extra functionality. The collections module provides specialized container datatypes that act as powerful alternatives.

One common task is counting items in a sequence. You might reach for a dictionary, but you have to check if a key exists before incrementing its value. A defaultdict simplifies this. When you create a defaultdict, you provide a function (like int or list) that's used to supply a default value for a key that doesn't exist. This avoids KeyError exceptions and cleans up your code.

from collections import defaultdict

words = ["apple", "banana", "apple", "orange", "banana", "apple"]

# Using defaultdict to count words
word_counts = defaultdict(int) # int() returns 0
for word in words:
    word_counts[word] += 1

print(word_counts) # Output: defaultdict(<class 'int'>, {'apple': 3, 'banana': 2, 'orange': 1})

Another useful tool is the namedtuple. Tuples are memory-efficient and immutable, making them good for fixed data records. However, accessing elements by index, like point[0] or point[1], can make code hard to read. A gives you the best of both worlds: the lightweight nature of a tuple with the readability of accessing attributes by name.

from collections import namedtuple

# Define a new namedtuple type called 'Point'
Point = namedtuple('Point', ['x', 'y'])

# Create an instance of it
p1 = Point(10, 20)

# Access data by name (more readable)
print(f"The x-coordinate is {p1.x}") # Output: The x-coordinate is 10

# You can still access by index (like a regular tuple)
print(f"The y-coordinate is {p1[1]}") # Output: The y-coordinate is 20

By mastering these data structures and techniques, you can write Python code that is not only functional but also efficient, readable, and idiomatic.

Quiz Questions 1/6

What is the primary advantage of using a set over a list when you need to store a collection of unique items?

Quiz Questions 2/6

Given the code below, what will be the output?

set1 = {1, 2, 3, 4}
set2 = {3, 4, 5, 6}
print(set1 ^ set2)