Lists and Tuples: Sequential Data Structures
Welcome to the foundation of Python data structures! Lists and tuples are like the bread and butter of data organization - they keep things in order.
Lists: Mutable Ordered Collections
Creating Lists
# Empty list
empty_list = []
# List with initial values
numbers = [1, 2, 3, 4, 5]
fruits = ["apple", "banana", "orange"]
mixed = [1, "hello", True, 3.14, [1, 2, 3]]
# Using list() constructor
from_range = list(range(5)) # [0, 1, 2, 3, 4]
from_string = list("hello") # ['h', 'e', 'l', 'l', 'o']
Accessing List Elements
fruits = ["apple", "banana", "orange", "grape", "kiwi"]
# Positive indexing (from start)
print(fruits[0]) # "apple"
print(fruits[2]) # "orange"
# Negative indexing (from end)
print(fruits[-1]) # "kiwi"
print(fruits[-2]) # "grape"
# Getting list length
print(len(fruits)) # 5
Modifying Lists
fruits = ["apple", "banana", "orange"]
# Change element
fruits[1] = "blueberry"
print(fruits) # ["apple", "blueberry", "orange"]
# Add elements
fruits.append("grape") # Add to end
print(fruits) # ["apple", "blueberry", "orange", "grape"]
fruits.insert(1, "avocado") # Insert at position 1
print(fruits) # ["apple", "avocado", "blueberry", "orange", "grape"]
# Remove elements
fruits.remove("orange") # Remove by value
print(fruits) # ["apple", "avocado", "blueberry", "grape"]
popped = fruits.pop() # Remove and return last element
print(f"Popped: {popped}") # "grape"
print(fruits) # ["apple", "avocado", "blueberry"]
popped_index = fruits.pop(1) # Remove and return element at index 1
print(f"Popped at index 1: {popped_index}") # "avocado"
List Slicing
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
# Basic slicing [start:end] (end not included)
print(numbers[2:5]) # [2, 3, 4]
print(numbers[:3]) # [0, 1, 2] (from start to index 3)
print(numbers[7:]) # [7, 8, 9] (from index 7 to end)
# With step [start:end:step]
print(numbers[::2]) # [0, 2, 4, 6, 8] (every 2nd element)
print(numbers[1::2]) # [1, 3, 5, 7, 9] (every 2nd starting from index 1)
print(numbers[::-1]) # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] (reverse)
# Replace multiple elements with slice
numbers[2:5] = [20, 30, 40]
print(numbers) # [0, 1, 20, 30, 40, 5, 6, 7, 8, 9]
# Delete multiple elements
numbers[2:5] = []
print(numbers) # [0, 1, 5, 6, 7, 8, 9]
List Methods
fruits = ["apple", "banana", "orange", "apple", "grape"]
# Count occurrences
print(fruits.count("apple")) # 2
# Find index of first occurrence
print(fruits.index("banana")) # 1
# Sort in place
fruits.sort()
print(fruits) # ['apple', 'apple', 'banana', 'grape', 'orange']
# Reverse in place
fruits.reverse()
print(fruits) # ['orange', 'grape', 'banana', 'apple', 'apple']
# Create sorted copy (doesn't modify original)
original = ["c", "a", "b"]
sorted_copy = sorted(original)
print("Original:", original) # ['c', 'a', 'b']
print("Sorted copy:", sorted_copy) # ['a', 'b', 'c']
# Extend list with another list
list1 = [1, 2, 3]
list2 = [4, 5, 6]
list1.extend(list2)
print(list1) # [1, 2, 3, 4, 5, 6]
# Clear all elements
list1.clear()
print(list1) # []
Tuples: Immutable Ordered Collections
Creating Tuples
# Empty tuple
empty_tuple = ()
# Single element tuple (note the comma!)
single = (42,)
single_alternative = 42, # Also works
# Multiple elements
coordinates = (10, 20, 30)
person = ("Alice", 25, "Engineer")
colors = "red", "green", "blue" # Parentheses optional
print(type(colors)) # <class 'tuple'>
Tuple Operations
point = (10, 20, 30)
# Access elements (same as lists)
print(point[0]) # 10
print(point[-1]) # 30
# Slicing (same as lists)
print(point[1:]) # (20, 30)
# Length
print(len(point)) # 3
# Count and index methods
numbers = (1, 2, 3, 2, 1)
print(numbers.count(2)) # 2
print(numbers.index(3)) # 2
# Tuples are immutable - cannot modify!
# point[0] = 15 # TypeError: 'tuple' object does not support item assignment
Tuple Unpacking
# Basic unpacking
point = (10, 20)
x, y = point
print(f"x: {x}, y: {y}") # x: 10, y: 20
# Extended unpacking
numbers = (1, 2, 3, 4, 5)
first, *middle, last = numbers
print(f"First: {first}") # 1
print(f"Middle: {middle}") # [2, 3, 4]
print(f"Last: {last}") # 5
# Unpacking in function returns
def get_user_info():
return "Alice", 25, "alice@email.com"
name, age, email = get_user_info()
print(f"Name: {name}, Age: {age}, Email: {email}")
When to Use Lists vs Tuples
Use Lists When:
# 1. You need to modify the collection
shopping_list = ["milk", "bread", "eggs"]
shopping_list.append("butter") # Can add items
shopping_list[0] = "almond milk" # Can modify items
# 2. The collection will change size
user_posts = [] # Start empty
user_posts.append("First post")
user_posts.append("Second post")
# 3. You need to sort or reverse
scores = [85, 92, 78, 96]
scores.sort() # [78, 85, 92, 96]
scores.reverse() # [96, 92, 85, 78]
Use Tuples When:
# 1. Data should not change (immutable)
# GPS coordinates
locations = [
(40.7128, -74.0060), # NYC
(34.0522, -118.2437) # LA
]
# 2. Function returns multiple values
def divide_with_remainder(a, b):
quotient = a // b
remainder = a % b
return quotient, remainder
result, remainder = divide_with_remainder(17, 5)
print(f"17 ÷ 5 = {result} remainder {remainder}")
# 3. Dictionary keys (lists cannot be keys)
student_grades = {
("Alice", "Smith"): "A",
("Bob", "Jones"): "B"
}
# 4. Unpacking assignments
name, age, city = ("Alice", 25, "New York")
List and Tuple Comprehensions
List Comprehensions
# Basic list comprehension
numbers = [1, 2, 3, 4, 5]
squares = [x**2 for x in numbers]
print(squares) # [1, 4, 9, 16, 25]
# With condition
even_squares = [x**2 for x in numbers if x % 2 == 0]
print(even_squares) # [4, 16]
# Nested comprehensions
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [num for row in matrix for num in row]
print(flattened) # [1, 2, 3, 4, 5, 6, 7, 8, 9]
# String operations
words = ["hello", "world", "python"]
upper_words = [word.upper() for word in words]
print(upper_words) # ['HELLO', 'WORLD', 'PYTHON']
Tuple Comprehensions (Generator Expressions)
# Tuple comprehensions create generators
numbers = [1, 2, 3, 4, 5]
squares_gen = (x**2 for x in numbers)
print(type(squares_gen)) # <class 'generator'>
# Convert to tuple
squares_tuple = tuple(squares_gen)
print(squares_tuple) # (1, 4, 9, 16, 25)
# Or iterate directly
for square in (x**2 for x in numbers):
print(square, end=" ") # 1 4 9 16 25
print()
Real-World Examples
Example 1: Task Manager
def add_task(tasks, task):
"""Add a task to the list."""
tasks.append(task)
print(f"Added task: {task}")
def complete_task(tasks, index):
"""Mark a task as completed and remove it."""
if 0 <= index < len(tasks):
completed = tasks.pop(index)
print(f"Completed: {completed}")
else:
print("Invalid task index")
def show_tasks(tasks):
"""Display all tasks with indices."""
if not tasks:
print("No tasks remaining!")
else:
print("Current tasks:")
for i, task in enumerate(tasks):
print(f"{i}. {task}")
# Usage
tasks = []
add_task(tasks, "Buy groceries")
add_task(tasks, "Finish homework")
add_task(tasks, "Call mom")
show_tasks(tasks)
complete_task(tasks, 1) # Complete "Finish homework"
show_tasks(tasks)
Example 2: Student Grade Analyzer
def calculate_average(grades):
"""Calculate average of grades."""
if not grades:
return 0
return sum(grades) / len(grades)
def find_highest(grades):
"""Find the highest grade."""
if not grades:
return None
return max(grades)
def get_grade_distribution(grades):
"""Categorize grades into letter grades."""
distribution = {"A": 0, "B": 0, "C": 0, "D": 0, "F": 0}
for grade in grades:
if grade >= 90:
distribution["A"] += 1
elif grade >= 80:
distribution["B"] += 1
elif grade >= 70:
distribution["C"] += 1
elif grade >= 60:
distribution["D"] += 1
else:
distribution["F"] += 1
return distribution
# Student data as list of tuples
students = [
("Alice", [85, 92, 88, 95]),
("Bob", [78, 85, 90, 82]),
("Charlie", [92, 96, 89, 94])
]
for name, grades in students:
avg = calculate_average(grades)
highest = find_highest(grades)
print(f"{name}: Average={avg:.1f}, Highest={highest}")
# Overall grade distribution
all_grades = [grade for _, grades in students for grade in grades]
distribution = get_grade_distribution(all_grades)
print(f"Grade distribution: {distribution}")
Example 3: Simple Database (Tuple of Lists)
# Simple in-memory database using tuples
def create_database():
"""Create a simple database structure."""
# Return tuple of (users, products, orders)
users = [] # List of (user_id, name, email) tuples
products = [] # List of (product_id, name, price) tuples
orders = [] # List of (order_id, user_id, product_id, quantity) tuples
return users, products, orders
def add_user(db, user_id, name, email):
"""Add a user to the database."""
users, _, _ = db
users.append((user_id, name, email))
print(f"Added user: {name}")
def add_product(db, product_id, name, price):
"""Add a product to the database."""
_, products, _ = db
products.append((product_id, name, price))
print(f"Added product: {name} (${price})")
def place_order(db, order_id, user_id, product_id, quantity):
"""Place an order."""
_, _, orders = db
orders.append((order_id, user_id, product_id, quantity))
print(f"Order placed: {quantity} x product {product_id} for user {user_id}")
def get_user_orders(db, user_id):
"""Get all orders for a user."""
_, _, orders = db
user_orders = [order for order in orders if order[1] == user_id]
return user_orders
# Usage
db = create_database()
add_user(db, 1, "Alice", "alice@email.com")
add_user(db, 2, "Bob", "bob@email.com")
add_product(db, 101, "Laptop", 999)
add_product(db, 102, "Mouse", 25)
place_order(db, 1001, 1, 101, 1) # Alice buys 1 laptop
place_order(db, 1002, 1, 102, 2) # Alice buys 2 mice
place_order(db, 1003, 2, 102, 1) # Bob buys 1 mouse
alice_orders = get_user_orders(db, 1)
print(f"Alice's orders: {alice_orders}")
Common Mistakes
1. Mutable Default Arguments
# ❌ Wrong - mutable default argument
def add_item(item, shopping_list=[]):
shopping_list.append(item)
return shopping_list
list1 = add_item("milk")
list2 = add_item("bread")
print(list1) # ['milk', 'bread'] - unexpected!
print(list2) # ['milk', 'bread'] - same list!
# ✅ Correct - use None as default
def add_item(item, shopping_list=None):
if shopping_list is None:
shopping_list = []
shopping_list.append(item)
return shopping_list
list1 = add_item("milk")
list2 = add_item("bread")
print(list1) # ['milk']
print(list2) # ['bread']
2. Modifying List While Iterating
# ❌ Dangerous - modifying list while iterating
numbers = [1, 2, 3, 4, 5]
for num in numbers:
if num % 2 == 0:
numbers.remove(num)
print(numbers) # [1, 3, 5] - but missing some evens?
# ✅ Safe - create new list or iterate backwards
numbers = [1, 2, 3, 4, 5]
evens = [num for num in numbers if num % 2 != 0]
print(evens) # [1, 3, 5]
3. Forgetting Tuple Comma
# ❌ Not a tuple
not_tuple = (42)
print(type(not_tuple)) # <class 'int'>
# ✅ Tuple with one element
single_tuple = (42,)
print(type(single_tuple)) # <class 'tuple'>
Performance Tips
List Operations Performance
# Fast operations (O(1))
my_list = [1, 2, 3]
my_list.append(4) # Fast - add to end
my_list.pop() # Fast - remove from end
len(my_list) # Fast - get length
# Slow operations (O(n))
my_list.insert(0, 0) # Slow - shift all elements
my_list.remove(2) # Slow - search and shift
2 in my_list # Slow - linear search
Choosing the Right Structure
# For fixed data that won't change
MONTHS = ("January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December")
# For data that needs modification
user_posts = [] # Start empty, will grow
# For coordinates (natural tuples)
coordinates = [(40.7128, -74.0060), (34.0522, -118.2437)]
# For lookup tables (use dict, not list of tuples)
country_codes = {"US": "United States", "CA": "Canada", "UK": "United Kingdom"}
Practice Exercises
Exercise 1: To-Do List
Create a to-do list application with:
- Add task
- Remove task by index
- Mark task as complete
- Show all tasks
- Clear all tasks
Exercise 2: Grade Calculator
Build a grade calculator that:
- Stores grades in a list
- Calculates average
- Finds highest/lowest grade
- Shows grade distribution (A, B, C, D, F)
Exercise 3: Shopping Cart
Create a shopping cart system:
- Add items (name, price, quantity)
- Remove items
- Calculate total price
- Apply discount
- Show cart contents
Exercise 4: Text Analyzer
Build a text analyzer that:
- Splits text into words
- Counts word frequencies
- Finds longest/shortest words
- Removes duplicates
- Sorts words alphabetically
Exercise 5: Matrix Operations
Create matrix operations:
- Create identity matrix
- Transpose matrix
- Add two matrices
- Multiply matrix by scalar
- Print matrix nicely
Summary
Lists and tuples are fundamental data structures:
Lists (mutable, ordered):
- Create:
[]orlist() - Modify:
append(),insert(),remove(),pop() - Access:
list[index], slicing[start:end:step] - Use for: collections that change
Tuples (immutable, ordered):
- Create:
()or comma-separated values - Access: same as lists
- Unpack:
a, b, c = tuple - Use for: fixed data, multiple returns, dictionary keys
Comprehensions:
- Lists:
[expression for item in iterable if condition] - Generators:
(expression for item in iterable if condition)
Choose based on your needs:
- Lists for dynamic collections
- Tuples for fixed, immutable data
- Comprehensions for concise transformations
Next: Dictionaries and Sets - key-value and unique data! 🔑