Intermediate

Data Structures

Master Python's built-in data structures: lists, tuples, dictionaries, and sets, along with comprehensions and performance considerations.

Lists

Lists are ordered, mutable sequences that can hold any type of element:

Python
# Creating lists
fruits = ["apple", "banana", "cherry"]
numbers = [1, 2, 3, 4, 5]
mixed = [1, "hello", True, 3.14]

# Indexing and slicing
print(fruits[0])       # apple
print(fruits[-1])      # cherry
print(fruits[1:3])    # ['banana', 'cherry']

# Common methods
fruits.append("date")        # Add to end
fruits.insert(1, "blueberry") # Insert at index
fruits.remove("banana")       # Remove by value
popped = fruits.pop()          # Remove and return last
fruits.extend(["fig", "grape"]) # Add multiple
fruits.sort()                   # Sort in place
fruits.reverse()                # Reverse in place
idx = fruits.index("apple")    # Find index
count = fruits.count("apple")  # Count occurrences

# Sorting
nums = [3, 1, 4, 1, 5]
sorted_nums = sorted(nums)             # Returns new list
sorted_desc = sorted(nums, reverse=True)  # Descending

Tuples

Tuples are ordered, immutable sequences — once created, they cannot be changed:

Python
# Creating tuples
point = (3, 4)
rgb = (255, 128, 0)
single = (42,)  # Note the comma for single-element tuple

# Unpacking
x, y = point
r, g, b = rgb

# Tuples as dictionary keys (immutable = hashable)
locations = {(40.7, -74.0): "New York", (51.5, -0.1): "London"}

# Named tuples (more readable)
from collections import namedtuple
Point = namedtuple("Point", ["x", "y"])
p = Point(3, 4)
print(p.x, p.y)  # 3 4

Dictionaries

Dictionaries are unordered key-value pairs with O(1) lookups:

Python
# Creating dictionaries
person = {"name": "Alice", "age": 30, "city": "NYC"}

# Access and modify
print(person["name"])          # Alice
print(person.get("email", "N/A"))  # N/A (safe access)
person["email"] = "alice@example.com"
del person["city"]

# Methods
print(person.keys())     # dict_keys(['name', 'age', 'email'])
print(person.values())   # dict_values(['Alice', 30, 'alice@...'])
print(person.items())    # dict_items([('name','Alice'), ...])

# Merge dictionaries (Python 3.9+)
defaults = {"theme": "dark", "lang": "en"}
overrides = {"theme": "light"}
settings = defaults | overrides  # {'theme': 'light', 'lang': 'en'}

# Looping
for key, value in person.items():
    print(f"{key}: {value}")

Sets

Sets are unordered collections of unique elements:

Python
# Creating sets
colors = {"red", "green", "blue"}
numbers = set([1, 2, 2, 3, 3])  # {1, 2, 3}

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

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

# Membership test (very fast - O(1))
print(3 in a)  # True

Comprehensions

Python
# List comprehension
squares = [x**2 for x in range(10)]

# Dict comprehension
word_lengths = {word: len(word) for word in ["hello", "world"]}
# {'hello': 5, 'world': 5}

# Set comprehension
unique_lengths = {len(w) for w in ["hi", "hey", "go"]}
# {2, 3}

When to Use Each

StructureOrderedMutableDuplicatesBest For
ListYesYesYesOrdered collections, stacks, queues
TupleYesNoYesFixed data, dict keys, return values
DictInsertionYesKeys: NoKey-value mappings, fast lookups
SetNoYesNoUnique elements, membership testing

Performance Comparison

OperationListDictSet
Lookup (in)O(n)O(1)O(1)
InsertO(1) append / O(n) insertO(1)O(1)
DeleteO(n)O(1)O(1)
IterationO(n)O(n)O(n)
Performance tip: If you need to check membership frequently (e.g., "is this item in my collection?"), use a set or dict instead of a list. The lookup goes from O(n) to O(1).