When you have more than one piece of data, you need a collection. Python’s most common collections are Lists and Tuples.

Lists

A List is an ordered collection of values. They are mutable, meaning you can add, remove, or change items after the list is created.

# Lists use square brackets []
inventory = ["Apples", "Bananas", "Cherries"]

# They can hold mixed types (though usually we keep them consistent)
user_info = ["Daniel", 28, True]

Accessing and Slicing

Just like strings, lists use indexes starting at 0.

fruits = ["Apple", "Banana", "Cherry", "Date"]

print(fruits[0])    # Apple
print(fruits[-1])   # Date

# Slicing [start:end]
print(fruits[1:3])  # ['Banana', 'Cherry'] (Index 1 and 2)

# "Start 3 from the end and stop before the last one"
print(fruits[-3:-1]) # ['Banana', 'Cherry']

# Get the last two items
print(fruits[-2:])   # ['Cherry', 'Date']

Common List Checks

You often might need to check what’s inside it or how big it is.

fruits = ["Apple", "Banana", "Cherry"]

# Check the length with len()
print(len(fruits)) # 3

# Check if an item exists (The 'in' keyword)
print("Apple" in fruits) # True
print("Orange" in fruits) # False

Modifying Lists

Because lists are mutable, we can update them on the fly:

fruits = ["Apple", "Banana"]

# Change an item
fruits[1] = "Blueberry" 

# Add to the end
fruits.append("Cherry") 

# Remove by value
fruits.remove("Apple")  

print(fruits) # ['Blueberry', 'Cherry']

Tuples

A Tuple looks like a list but uses parentheses (). The big difference is that Tuples are immutable (once they are created, they cannot be changed).

# A tuple of coordinates
coordinates = (10.5, 20.2)

# This will crash your program:
# coordinates[0] = 15.0 
# TypeError: 'tuple' object does not support item assignment

You can’t change the tuple, but you can reassign it.

point = (1, 2)

# I can't change the 1 or the 2 inside the tuple.
# But I can tell the name 'point' to look at a brand-new tuple:
point = (5, 6)

If you want to create a tuple with just one item, you must include a trailing comma. Without it, Python just thinks you are using parentheses for math.

not_a_tuple = (5)   # This is just the integer 5
is_a_tuple = (5,)   # This is a tuple containing 5
print(type(is_a_tuple)) # <class 'tuple'>

Because tuples are “fixed,” Python handles them slightly faster and uses less memory.

Use tuples for data that shouldn’t change.

Nested Lists

When you put a list inside another list, you create a “2D” structure. To get to the data, you use two sets of square brackets: list[row][column]

# A 3x3 grid (like a Tic-Tac-Toe board)
board = [
    ["X", "O", "X"], # Index 0
    [" ", "X", "O"], # Index 1
    ["O", " ", " "]  # Index 2
]

# Access the first sub-list
print(board[0])    # ["X", "O", "X"]

# Access a specific item inside that sub-list
# board[row_index][item_index]
print(board[0][1]) # "O"

You can change these values just like a normal list:

board[1][0] = "O" # Sets the first item of the second row to "O"

Here is a famous “gotcha.” If you put a list inside a tuple, you can’t change which list is there, but you can change the items inside that list.

my_tuple = (1, 2, ["a", "b"])
my_tuple[2][0] = "Z" # This actually works!