10xdev Book: Programming from Zero to Pro with Python

Chapter 7: Python Data Structures: Lists, Tuples, and Dictionaries

Chapter Introduction

So far, our variables have held just one piece of information at a time – a single name, number, or boolean. But the real world is messy! Think about a shopping list, a contact entry in your phone, or even the players in a game. You need ways to store and manage collections of related data.

That’s where Data Structures come in. They are Python’s built-in containers designed to hold multiple values in an organized way. Mastering them is key to writing effective code for almost any task.

In this chapter, we’ll explore the three most fundamental Python data structures you’ll use constantly:

  1. Lists (list): Your flexible, ordered, go-to container for items you might need to change.
  2. Tuples (tuple): Simple, ordered containers for items that should never change once created.
  3. Dictionaries (dict): Super useful containers for storing data as key-value pairs, perfect for lookups.

Let’s dive into how to use these powerful tools.


1. Lists (list): Your Flexible Friend

A list is exactly what it sounds like – an ordered sequence of items. It’s the most versatile collection type in Python.

Key Features:

  • Ordered: Items maintain the order you add them.
  • Mutable: You can change the list after it’s created – add, remove, or modify items. 👍
  • Allows Duplicates: You can have the same item multiple times.
  • Heterogeneous: Can hold items of different data types (though often you’ll store items of the same type).

Creating Lists: Use square brackets []. Use type hints (list[type]) for clarity.

# List of strings
tasks: list[str] = ["Learn Python", "Practice coding", "Build a project"]

# List of integers
scores: list[int] = [95, 88, 72, 95] # Duplicates allowed

# List of mixed types (less common but possible)
mixed_list: list = ["Apple", 3, True, 4.5]

# Empty list
empty_list: list = []

print(tasks)
print(scores)

Working with Lists

  • Accessing Items (Indexing): Use the index number (starting from 0).
    print(f"First task: {tasks[0]}")      # -> Learn Python
    print(f"Last score: {scores[-1]}")    # -> 95 (Negative indexing)
    
  • Slicing: Get a sub-list using [start:stop_before].
    first_two_tasks = tasks[:2] # -> ['Learn Python', 'Practice coding']
    print(first_two_tasks)
    
  • Modifying Items: Because lists are mutable.
    scores[2] = 75 # Change the third score
    print(f"Updated scores: {scores}")
    
  • Adding Items:
    • .append(item): Adds to the end. (Most common)
      tasks.append("Read documentation")
      print(f"Tasks after append: {tasks}")
      
    • .insert(index, item): Adds at a specific position.
      tasks.insert(1, "Review concepts") # Insert at index 1
      print(f"Tasks after insert: {tasks}")
      
  • Removing Items:
    • .remove(value): Removes the first occurrence of a specific value.
      scores.remove(95) # Removes the first 95
      print(f"Scores after remove: {scores}")
      
    • .pop(index): Removes and returns the item at an index (default: last item).
      last_task = tasks.pop() # Remove and get the last item
      print(f"Removed task: {last_task}")
      print(f"Tasks after pop: {tasks}")
      
    • del list_name[index]: Deletes the item at an index.
      del scores[0] # Delete the first score
      print(f"Scores after del: {scores}")
      
  • Length: len(list_name) gives the number of items.
    print(f"Number of remaining tasks: {len(tasks)}")
    
  • Checking Membership: Use in.
    if "Learn Python" in tasks:
        print("Learning Python is still on the list!")
    
  • Looping: Use a for loop.
    print("\nRemaining Tasks:")
    for task in tasks:
        print(f"- {task}")
    

2. Tuples (tuple): The Immutable Sequence

A tuple is very similar to a list – it’s an ordered sequence of items. The crucial difference? Tuples are immutable. Once you create a tuple, you cannot change its contents. 🚫

Why Use Tuples?

  • Data Integrity: Guarantees that a sequence of values won’t accidentally change (e.g., RGB color values, coordinates, fixed configuration settings).
  • Dictionary Keys: Immutable types like tuples can be used as keys in dictionaries (lists cannot).
  • Performance: Sometimes slightly faster than lists for iteration (though usually not a significant factor).

Creating Tuples: Use parentheses (). For a single-item tuple, you need a trailing comma (item,).

# Tuple of coordinates
point: tuple[float, float] = (10.5, 25.0)

# Tuple of RGB values
color_red: tuple[int, int, int] = (255, 0, 0)

# Single item tuple (note the comma!)
single_tuple: tuple[str] = ("hello",)

print(point)
print(color_red)

# Accessing works like lists
print(f"X-coordinate: {point[0]}")

# BUT, trying to change an item causes a TypeError!
# point[0] = 15.0 # This line would CRASH!

Tuple Unpacking

A handy feature where you assign elements of a tuple (or list) to individual variables directly.

x, y = point # Assigns 10.5 to x, 25.0 to y
print(f"Unpacked: x={x}, y={y}")

r, g, b = color_red
print(f"Red={r}, Green={g}, Blue={b}")

3. Dictionaries (dict): Key-Value Pairs

A dictionary is an unordered (in older Python, but ordered by insertion in Python 3.7+) collection of key-value pairs. Think of it like a real dictionary: you look up a key (the word) to get its value (the definition). Keys must be unique and immutable (strings, numbers, tuples are common keys).

Key Features:

  • Key-Value Mapping: Each item links a unique key to a value.
  • Mutable: You can add, remove, and modify key-value pairs.
  • Fast Lookups: Extremely efficient at finding a value if you know its key. 👍
  • Ordered (Python 3.7+): Remembers the order items were inserted.

Creating Dictionaries: Use curly braces {} with key: value pairs. Use type hints (dict[key_type, value_type]).

# Dictionary representing a student
student: dict[str, str | int] = {
    "name": "Fatima",
    "id": 12345,
    "major": "Computer Science"
}

# Empty dictionary
empty_dict: dict = {}

print(student)

Working with Dictionaries

  • Accessing Values: Use the key inside square brackets []. Causes KeyError if key doesn’t exist.
    print(f"Student Name: {student['name']}")
    # print(student['age']) # This would cause a KeyError
    
  • Safe Access (.get()): Use .get(key, default) to avoid KeyError. Returns None or your specified default if the key is missing. This is the preferred way.
    major = student.get("major", "Undeclared")
    age = student.get("age") # Returns None if 'age' key doesn't exist
    print(f"Major: {major}")
    print(f"Age: {age}") # Output: Age: None
    
  • Adding/Modifying Items: Assign a value using the key.
    student["id"] = 54321      # Modify existing key
    student["email"] = "[email protected]" # Add new key-value pair
    print(f"Updated student: {student}")
    
  • Removing Items:
    • del dictionary[key]: Removes the key-value pair. Causes KeyError if key doesn’t exist.
      del student["major"]
      print(f"Student after del: {student}")
      
    • .pop(key, default): Removes the key and returns its value. Can provide a default to avoid KeyError.
      email = student.pop("email", None)
      print(f"Removed email: {email}")
      print(f"Student after pop: {student}")
      
  • Length: len(dictionary) gives the number of key-value pairs.
  • Checking Key Existence: Use in.
    if "name" in student:
        print("Name key exists.")
    
  • Looping:
    • Looping over keys (default):
      print("\nKeys:")
      for key in student:
          print(key)
      
    • Looping over values (.values()):
      print("\nValues:")
      for value in student.values():
          print(value)
      
    • Looping over key-value pairs (.items()): (Most common)
      print("\nItems (Key-Value pairs):")
      for key, value in student.items():
          print(f"{key} -> {value}")
      

4. Applied Exercise: Product Inventory (Revisited)

Let’s refine our inventory example using type hints and clearer structure. We’ll store products as a list of dictionaries.

# Define the type hint for a single product dictionary
Product = dict[str, str | int | float]
# Define the type hint for the inventory list
Inventory = list[Product]

# --- Inventory Management ---

# Initialize inventory (could be loaded from a file later)
inventory: Inventory = [
    {"name": "Laptop", "price": 4500.00, "quantity": 10},
    {"name": "Mouse", "price": 120.50, "quantity": 50},
    {"name": "Keyboard", "price": 250.00, "quantity": 30}
]

def add_product(inv: Inventory, name: str, price: float, quantity: int):
    """Adds a new product dictionary to the inventory list."""
    new_product: Product = {"name": name, "price": price, "quantity": quantity}
    inv.append(new_product)
    print(f"Added product: {name}")

def display_inventory(inv: Inventory):
    """Prints the current inventory and calculates total value."""
    print("\n--- Current Store Inventory ---")
    total_value: float = 0.0
    if not inv:
        print("Inventory is empty.")
        return

    for item in inv:
        item_stock_value = item.get("price", 0) * item.get("quantity", 0)
        total_value += item_stock_value
        print(
            f"- Item: {item.get('name', 'N/A')}, "
            f"Price: {item.get('price', 0):.2f} DH, "
            f"Quantity: {item.get('quantity', 0)}, "
            f"Stock Value: {item_stock_value:.2f} DH"
        )
    print("-------------------------------")
    print(f"Total value of all products: {total_value:.2f} DH")

# --- Run the example ---
display_inventory(inventory)
add_product(inventory, name="Webcam", price=350.00, quantity=15)
display_inventory(inventory)

5. Chapter Summary

  • Lists ([]): Ordered, mutable, general-purpose sequence. Use when order matters and items might change.
  • Tuples (()): Ordered, immutable sequence. Use for fixed collections or when you need hashable items (like dictionary keys).
  • Dictionaries ({}): Unordered (pre-3.7) / Insertion-ordered (3.7+), mutable collection of unique key-value pairs. Use for fast lookups based on a key.
  • Understanding mutability (can it be changed?) is key to choosing the right structure.
  • Combine these structures (e.g., list of dictionaries) to model complex data.

You can now manage collections of data effectively in memory. But what happens when your program closes? All that data is lost! To make your information persistent, we need to learn how to read from and write to files. That’s coming up next!