Programming with Python | Chapter 5: Data Structures – Lists and Tuples

Chapter Objectives

  • Understand the concept of data structures for organizing collections of items.
  • Learn how to create and manipulate lists: ordered, mutable sequences.
  • Access list elements using indexing and slicing, similar to strings.
  • Modify lists using methods like .append(), .insert(), .remove(), .pop(), and index assignment.
  • Sort and reverse lists using .sort() and .reverse().
  • Learn how to create and use tuples: ordered, immutable sequences.
  • Understand the key differences between lists and tuples, particularly mutability.
  • Recognize appropriate use cases for lists and tuples.
  • Use the len() function with lists and tuples.

Introduction

While variables hold single pieces of data, we often need to work with collections of related items. Data structures provide ways to organize and store multiple values. This chapter introduces two fundamental sequence-based data structures in Python: lists and tuples. Both allow you to store an ordered collection of items, but they differ primarily in their mutability: lists can be changed after creation, while tuples cannot. We will explore how to create, access, modify (for lists), and use these versatile structures.

Theory & Explanation

What are Data Structures?

Data structures are specialized formats for organizing, processing, retrieving, and storing data. They allow us to manage collections of data efficiently. Python offers several built-in data structures, and lists and tuples are often the first ones programmers learn because they represent ordered sequences.

Lists: Ordered, Mutable Collections

A list is an ordered sequence of items, enclosed in square brackets [], with items separated by commas. Lists are highly flexible because:

  • Ordered: Items maintain their position (index).
  • Mutable: You can change the list after it’s created (add, remove, or modify items).
  • Heterogeneous: Items in a list can be of different data types (though often they are homogeneous for clarity).

Creating Lists:

Python
empty_list = []
numbers = [1, 2, 3, 5, 8]
mixed_list = [1, "hello", 3.14, True, None]
list_of_lists = [[1, 2], [3, 4], [5, 6]] # Nested list

print(empty_list)
print(numbers)
print(mixed_list)

Accessing Elements (Indexing and Slicing):

Lists use the same zero-based indexing and slicing syntax as strings (Chapter 3).

Python
fruits = ["apple", "banana", "cherry", "date", "elderberry"]
# Indices:   0        1         2        3         4
# Neg Indices:-5       -4        -3       -2        -1

print(f"First fruit: {fruits[0]}")      # apple
print(f"Last fruit: {fruits[-1]}")     # elderberry
print(f"Middle fruits: {fruits[1:4]}") # ['banana', 'cherry', 'date'] (index 4 is exclusive)
print(f"First three: {fruits[:3]}")    # ['apple', 'banana', 'cherry']
print(f"From index 2 onwards: {fruits[2:]}") # ['cherry', 'date', 'elderberry']
print(f"Every other fruit: {fruits[::2]}") # ['apple', 'cherry', 'elderberry']

Modifying Lists (Mutability):

Because lists are mutable, you can change them directly.

Changing an Item:

Use index assignment.

Python
fruits[1] = "blueberry" # Replace 'banana'
print(fruits) # ['apple', 'blueberry', 'cherry', 'date', 'elderberry']

Adding Items:

  • .append(item): Adds item to the end of the list.
  • .insert(index, item): Inserts item at the specified index, shifting subsequent items.
  • .extend(iterable): Appends all items from another iterable (like another list) to the end.
Python
fruits.append("fig")
print(fruits) # ['apple', 'blueberry', 'cherry', 'date', 'elderberry', 'fig']

fruits.insert(2, "mango") # Insert 'mango' at index 2
print(fruits) # ['apple', 'blueberry', 'mango', 'cherry', 'date', 'elderberry', 'fig']

more_fruits = ["grape", "honeydew"]
fruits.extend(more_fruits)
print(fruits) # ['apple', ..., 'fig', 'grape', 'honeydew']

Removing Items:

  • .remove(value): Removes the first occurrence of the specified value. Raises ValueError if the value is not found.
  • .pop(index=-1): Removes and returns the item at the specified index. If no index is given, it removes and returns the last item. Raises IndexError if the index is invalid or the list is empty.
  • .del list_name[index]: Deletes the item at the specified index. Can also delete slices (del list_name[start:stop]).
Python
fruits.remove("date")
print(fruits) # ['apple', 'blueberry', 'mango', 'cherry', 'elderberry', 'fig', 'grape', 'honeydew']

last_fruit = fruits.pop() # Remove and get 'honeydew'
print(f"Removed fruit: {last_fruit}")
print(fruits) # ['apple', ..., 'grape']

second_fruit = fruits.pop(1) # Remove and get 'blueberry' at index 1
print(f"Removed second fruit: {second_fruit}")
print(fruits) # ['apple', 'mango', 'cherry', 'elderberry', 'fig', 'grape']

del fruits[0] # Delete 'apple' at index 0
print(fruits) # ['mango', 'cherry', 'elderberry', 'fig', 'grape']

Indexing and Slicing

Indices in Python Sequences

0 apple -5
1 banana -4
2 cherry -3
3 date -2
4 elder -1
fruits = [“apple”, “banana”, “cherry”, “date”, “elder”] print(fruits[0]) # Output: “apple” print(fruits[-1]) # Output: “elder” print(fruits[1:4]) # Output: [“banana”, “cherry”, “date”] print(fruits[:3]) # Output: [“apple”, “banana”, “cherry”] print(fruits[2:]) # Output: [“cherry”, “date”, “elder”] print(fruits[::2]) # Output: [“apple”, “cherry”, “elder”]

Other Common List Methods:

Common List Methods

Method Description Example Result
len(list) Returns the number of items len([3, 1, 4, 1, 5]) 5
list.append(item) Adds item to the end a=[1,2]; a.append(3) a is [1, 2, 3]
list.insert(idx, item) Inserts item at index idx a=[1,3]; a.insert(1, 2) a is [1, 2, 3]
list.extend(iterable) Adds all items from iterable a=[1,2]; a.extend([3,4]) a is [1, 2, 3, 4]
list.remove(value) Removes first occurrence of value a=[1,2,2]; a.remove(2) a is [1, 2]
list.pop([idx]) Removes & returns item at idx (default: last) a=[1,2,3]; x=a.pop(1) x is 2, a is [1, 3]
list.index(value) Returns index of first occurrence of value [1, 2, 2].index(2) 1
list.count(value) Counts occurrences of value [1, 2, 2].count(2) 2
list.sort() Sorts list in-place a=[3,1,2]; a.sort() a is [1, 2, 3]
list.reverse() Reverses list in-place a=[1,2,3]; a.reverse() a is [3, 2, 1]
list.copy() Creates a shallow copy a=[1,2]; b=a.copy() b is [1, 2] (new list)
list.clear() Removes all items from list a=[1,2,3]; a.clear() a is []

Note: Methods like .sort(), .reverse(), .clear() modify the list in-place and return None.

Note on sort() and reverse(): These methods modify the list in-place and return None. If you want a sorted copy without changing the original, use the sorted() function: sorted_nums = sorted(nums).

Tuples: Ordered, Immutable Collections

A tuple is an ordered sequence of items, enclosed in parentheses (), with items separated by commas. Tuples are like lists, but with one crucial difference:

  • Ordered: Items maintain their position (index).
  • Immutable: Once a tuple is created, you cannot change its contents (no adding, removing, or modifying items).
  • Heterogeneous: Items can be of different data types.

Creating Tuples:

Python
empty_tuple = ()
point = (10, 20) # Represents coordinates, for example
mixed_tuple = (1, "hello", 3.14, True)
single_item_tuple = (42,) # NOTE the trailing comma! Without it, (42) is just the integer 42.

print(point)
print(mixed_tuple)
print(single_item_tuple)
print(type((42))) # <class 'int'>
print(type((42,))) # <class 'tuple'>

Visual Examples

(
)
(
10
20
)
(
1
“hello”
3.14
True
)
(
42
,
)

Accessing Elements (Indexing and Slicing):

Tuples use the same indexing and slicing syntax as lists and strings.

Python
colors = ("red", "green", "blue")
# Indices:  0      1        2
# Neg Indices:-3     -2       -1

print(colors[0])    # red
print(colors[-1])   # blue
print(colors[0:2])  # ('red', 'green')

Immutability in Action:

Attempting to modify a tuple after creation results in a TypeError.

Python
# colors[0] = "yellow" # This will raise a TypeError: 'tuple' object does not support item assignment
# colors.append("purple") # This will raise an AttributeError: 'tuple' object has no attribute 'append'

Why Use Tuples?

If tuples are immutable, why use them instead of lists?

  1. Data Integrity: Immutability guarantees that the tuple’s contents won’t accidentally change, which is useful for representing fixed collections like coordinates, RGB color values, or records.
  2. Performance: Tuples can be slightly more memory-efficient and faster to iterate over than lists (though this difference is often minor in practice).
  3. Dictionary Keys: Because they are immutable, tuples containing only immutable elements (like strings, numbers, other tuples) can be used as keys in dictionaries (Chapter 6), whereas lists cannot.
  4. Function Arguments/Return Values: Often used to pass or return multiple fixed values from functions.

Tuple Methods:

Tuples have far fewer methods than lists due to their immutability. The main ones are:

Method Description Example Result
len(tuple) Returns the number of items len((1, 2, 3)) 3
tuple.count(value) Counts occurrences of value (1, 2, 2, 3).count(2) 2
tuple.index(value) Returns index of first occurrence (1, 2, 3).index(2) 1

Tuples have fewer methods than lists because they are immutable.

Tuple Unpacking

A convenient feature is tuple unpacking, which allows you to assign items from a tuple (or list) to individual variables.

Python
coordinates = (100, 200, 50) # x, y, z

x, y, z = coordinates # Unpacking

print(f"x: {x}, y: {y}, z: {z}") # x: 100, y: 200, z: 50

# This also works with lists
dimensions = [30, 40]
width, height = dimensions
print(f"Width: {width}, Height: {height}") # Width: 30, Height: 40

The number of variables on the left must match the number of items in the sequence.

Lists vs Tuples Comparison

Feature Lists [ ] Tuples ( )
Mutability Mutable (can be changed) Immutable (cannot be changed)
Syntax [1, 2, 3] (1, 2, 3)
Single item [42] (42,) Needs trailing comma!
Add elements Yes .append(), .insert(), .extend() No Cannot add elements
Remove elements Yes .pop(), .remove(), del No Cannot remove elements
Sorting Yes .sort() No Cannot sort in-place
Indexing Yes my_list[0] Yes my_tuple[0]
Slicing Yes my_list[1:3] Yes my_tuple[1:3]
Use as dictionary key No Yes (if containing only immutable elements)
Performance Slightly slower Slightly faster
Common uses Collection that needs to be modified Fixed data, function returns, coordinates

Code Examples

Example 1: List Manipulation

Python
# shopping_list.py

# Start with an initial list
shopping_items = ["milk", "bread", "eggs"]
print(f"Initial list: {shopping_items}")

# Add items
shopping_items.append("apples")
shopping_items.insert(1, "butter") # Insert at index 1
print(f"After adding: {shopping_items}")

# Check if an item exists
item_to_find = "bread"
if item_to_find in shopping_items: # The 'in' operator checks for membership
    print(f"'{item_to_find}' is in the list.")
    # Remove the item
    shopping_items.remove(item_to_find)
    print(f"After removing '{item_to_find}': {shopping_items}")
else:
    print(f"'{item_to_find}' not found.")

# Get and remove the last item
last_item = shopping_items.pop()
print(f"Bought '{last_item}'. Remaining: {shopping_items}")

# Sort the list alphabetically
shopping_items.sort()
print(f"Sorted list: {shopping_items}")

# Get the length
print(f"Number of items left: {len(shopping_items)}")

Explanation:

  • Demonstrates creating a list and modifying it using .append(), .insert(), .remove(), and .pop().
  • Uses the in keyword (a membership operator) to check if an item exists before trying to remove it.
  • Shows how .pop() removes and returns an item.
  • Uses .sort() to sort the list alphabetically in place.
  • Uses len() to find the number of items.

Example 2: Tuples for Fixed Data and Unpacking

Python
# coordinates.py

# Representing fixed points using tuples
point1 = (10, 20)
point2 = (50, -15)

# Function that returns multiple values as a tuple
def get_screen_dimensions():
    width = 1920
    height = 1080
    return (width, height) # Return a tuple

# Get dimensions and unpack
screen_w, screen_h = get_screen_dimensions()
print(f"Screen dimensions: Width={screen_w}, Height={screen_h}")

# Calculate distance (simplified example, not real distance formula)
x_diff = point2[0] - point1[0]
y_diff = point2[1] - point1[1]
print(f"Difference in coordinates: dx={x_diff}, dy={y_diff}")

# Trying to modify a tuple (will cause error if uncommented)
# point1[0] = 15 # TypeError

# Using tuples as dictionary keys (preview for Chapter 6)
locations = {
    (40.7128, -74.0060): "New York City",
    (34.0522, -118.2437): "Los Angeles"
}
print(f"Location at (40.7128, -74.0060): {locations[(40.7128, -74.0060)]}")

Explanation:

  • Uses tuples point1 and point2 to store fixed coordinate pairs.
  • A function get_screen_dimensions returns multiple values packaged in a tuple.
  • Tuple unpacking (screen_w, screen_h = ...) assigns the returned values directly to variables.
  • Accesses tuple elements using indexing (point1[0]).
  • Demonstrates (by commenting out the error line) that tuples are immutable.
  • Shows a common use case: using tuples (which are hashable) as keys in a dictionary.

Common Mistakes or Pitfalls

  • Mutability Confusion: Forgetting whether lists are mutable and tuples are immutable. Trying to change a tuple or expecting list methods like .append() to work on tuples.
  • .sort() vs sorted(): Calling my_list.sort() and expecting it to return the sorted list (it returns None and sorts in-place). Use the sorted(my_list) function to get a new sorted list without changing the original.
  • IndexError: Accessing a list or tuple index that is out of range.
  • ValueError: Using .remove(value) when value is not in the list, or using .index(value) when value is not present. Use the in operator first to check for existence if necessary.
  • Shallow Copies: Using new_list = old_list.copy() creates a shallow copy. If the list contains mutable objects (like other lists), modifying those inner objects will affect both the original and the copy. Deep copies require the copy module.
  • Single-Item Tuple Syntax: Forgetting the trailing comma when defining a tuple with only one item: my_tuple = (item) creates just the item, not a tuple; use my_tuple = (item,).

Chapter Summary

  • Data structures organize collections of data.
  • Lists ([]) are ordered, mutable sequences. They can hold items of different types.
    • Accessed via indexing and slicing.
    • Modified using index assignment, .append(), .insert(), .extend(), .remove(), .pop(), del.
    • Methods like .sort(), .reverse(), .index(), .count(), .copy() provide further operations.
  • Tuples (()) are ordered, immutable sequences.
    • Accessed via indexing and slicing.
    • Cannot be changed after creation.
    • Useful for fixed data, dictionary keys, function return values.
    • Key methods: .count(), .index(). Remember the trailing comma for single-item tuples: (item,).
  • The len() function works on both lists and tuples to get their length.
  • Tuple unpacking allows assigning sequence items to individual variables (x, y = my_tuple).

Exercises & Mini Projects

Exercises

  1. List Operations: Create a list of numbers, e.g., [10, 20, 30, 40, 50].
    • Print the third element.
    • Add the number 60 to the end of the list.
    • Insert the number 15 at index 1.
    • Remove the number 30 from the list.
    • Print the final list.
  2. Tuple Basics: Create a tuple containing the names of the first three months (“January”, “February”, “March”).
    • Print the tuple.
    • Print the second month using indexing.
    • Try to change the first month to “Jan” and observe the TypeError.
  3. List Slicing and Methods: Create a list letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g'].
    • Print a slice containing ‘c’, ‘d’, ‘e’.
    • Print a slice containing the first three letters.
    • Use .pop() to remove and print the last letter.
    • Use .append() to add the removed letter back.
    • Reverse the list in-place using .reverse() and print it.
  4. Tuple Unpacking: Create a tuple record = ("Alice", 30, "Engineer"). Unpack this tuple into three variables: name, age, and job. Print each variable on a separate line.
  5. List Membership: Create a list of approved usernames: approved = ["user1", "admin", "guest"]. Ask the user to input a username. Use the in operator and an if-else statement to print whether the entered username is “Approved” or “Not Approved”.

Mini Project: To-Do List Manager (Simple)

Goal: Create a very basic command-line to-do list manager using a Python list.

Steps:

  1. Initialize an empty list called tasks.
  2. Present the user with a simple menu (using print statements):
    • 1: Add Task
    • 2: View Tasks
    • 3: Remove Task (by index)
    • 4: Exit
  3. Use an input() prompt to get the user’s choice.
  4. Use an if-elif-else structure based on the user’s choice:
    • Choice 1 (Add):
      • Ask the user for the task description using input().
      • Use .append() to add the task to the tasks list.
      • Print a confirmation message.
    • Choice 2 (View):
      • Check if the tasks list is empty (using if not tasks: or if len(tasks) == 0:). If empty, print “No tasks yet!”.
      • If not empty, print “— Your Tasks —“.
      • Iterate through the list (we’ll cover loops formally soon, but for now, you can manually print elements or just print the whole list: print(tasks)). A better way for now: print each task with its index, e.g., print(f"1: {tasks[0]}"), print(f"2: {tasks[1]}"), etc. (This part is tricky without loops, so just printing the list is acceptable for this stage).
    • Choice 3 (Remove):
      • Check if the list is empty. If so, print “No tasks to remove.”
      • If not empty, ask the user for the index (number) of the task to remove. Convert the input to an int.
      • Important: Validate the index. Check if the entered index is valid (e.g., 0 <= index < len(tasks)). If invalid, print an error message.
      • If the index is valid, use .pop(index) to remove the task. Print a confirmation message showing the removed task.
    • Choice 4 (Exit):
      • Print “Exiting program.”
      • (We’ll learn how to properly exit loops later; for now, the program will just end after this choice).
    • Else (Invalid Choice):
      • Print “Invalid choice. Please enter a number between 1 and 4.”
  5. (Ideally, this would loop, but without loops yet, the program will run once per choice).

(Note: This project anticipates loops, which are covered next. The “View Tasks” and “Remove Task” parts are simplified here. Focus on using list methods correctly based on user input.)

Additional Sources:

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top