Python: Zero to Hero
Home/The Foundation
Share

Chapter 6: Lists — Your First Collection

So far every variable has held exactly one value — one number, one string, one boolean. That works for simple programs, but real programs deal with collections of things: a shopping cart with many items, a class with many students, a playlist with many songs.

Python's answer is the list. A list stores many values in a single variable, keeps them in order, and lets you do powerful things with them — add, remove, sort, search, slice, and loop through every item.

Lists are one of the most used data structures in all of Python. Master this chapter and you'll use what you learn here every single day.

What is a List?

A list is an ordered collection of items. You write it with square brackets [], with items separated by commas.

fruits = ["apple", "banana", "cherry"]
print(fruits)

Output:

['apple', 'banana', 'cherry']

One variable. Three values. That's the idea.

Lists can hold any type of data — numbers, strings, booleans, even other lists. They can also mix types, though in practice you usually keep one type per list.

numbers = [10, 20, 30, 40, 50]
names = ["Alice", "Bob", "Carlos"]
mixed = [1, "hello", True, 3.14]
empty = []

In plain English: A list is like a numbered to-do list. Each item has a position, and you can look up, change, add, or remove items by their position.

Your turn: Create a list called favorites with three of your favorite movies. Print the whole list.

Indexing — Getting One Item

Every item in a list has a position called its index. Python starts counting from 0.

fruits = ["apple", "banana", "cherry", "date", "elderberry"]
#           0         1         2        3          4

Use square brackets with the index to get one item.

print(fruits[0])   # apple
print(fruits[1])   # banana
print(fruits[4])   # elderberry

Negative indexing counts from the end. -1 is the last item, -2 is second to last, and so on.

print(fruits[-1])   # elderberry  (last)
print(fruits[-2])   # date        (second to last)
print(fruits[-5])   # apple       (same as index 0)

This is incredibly useful — you can always get the last item of any list with [-1] without knowing the length.

What happens if you use an index that doesn't exist?

print(fruits[10])   # IndexError: list index out of range

Python crashes with an IndexError. Always make sure your index is between 0 and len(list) - 1 (or between -len(list) and -1 for negatives).

Your turn: Create a list of five animals. Print the first animal, the last animal using a negative index, and the middle animal.

Slicing — Getting Multiple Items

A slice extracts a portion of a list and gives you a new list.

fruits = ["apple", "banana", "cherry", "date", "elderberry"]

print(fruits[1:4])    # ['banana', 'cherry', 'date']
print(fruits[0:3])    # ['apple', 'banana', 'cherry']
print(fruits[:3])     # ['apple', 'banana', 'cherry']  (start defaults to 0)
print(fruits[2:])     # ['cherry', 'date', 'elderberry']  (stop defaults to end)
print(fruits[:])      # ['apple', 'banana', 'cherry', 'date', 'elderberry']  (entire list)

The syntax is list[start:stop]. The start index is included. The stop index is excluded — same rule as range().

You can also add a step:

print(fruits[::2])    # ['apple', 'cherry', 'elderberry']  (every 2nd item)
print(fruits[::-1])   # ['elderberry', 'date', 'cherry', 'banana', 'apple']  (reversed)

[::-1] reverses a list. It's a common Python idiom.

Your turn: Create a list of ten numbers (0 through 9). Use slicing to print: the first three, the last three, and every other number.

Changing Items

Lists are mutable — you can change their contents after creation. Assign directly to an index.

colors = ["red", "green", "blue"]
colors[1] = "yellow"
print(colors)   # ['red', 'yellow', 'blue']

You can also replace a slice with new values:

numbers = [1, 2, 3, 4, 5]
numbers[1:3] = [20, 30]
print(numbers)   # [1, 20, 30, 4, 5]

Adding Items

append() — Add one item to the end

fruits = ["apple", "banana"]
fruits.append("cherry")
print(fruits)   # ['apple', 'banana', 'cherry']

insert() — Add one item at a specific position

fruits = ["apple", "banana", "cherry"]
fruits.insert(1, "blueberry")   # insert at index 1
print(fruits)   # ['apple', 'blueberry', 'banana', 'cherry']

Everything from index 1 onward shifts one position to the right.

extend() — Add all items from another list

fruits = ["apple", "banana"]
more_fruits = ["cherry", "date"]
fruits.extend(more_fruits)
print(fruits)   # ['apple', 'banana', 'cherry', 'date']

Or use + to combine two lists into a new one:

combined = ["apple", "banana"] + ["cherry", "date"]
print(combined)   # ['apple', 'banana', 'cherry', 'date']

Your turn: Start with an empty list. Append five numbers to it one at a time using a loop and range(). Print the final list.

Removing Items

remove() — Remove by value

Removes the first occurrence of the item.

fruits = ["apple", "banana", "cherry", "banana"]
fruits.remove("banana")
print(fruits)   # ['apple', 'cherry', 'banana']

If the item doesn't exist, Python raises a ValueError.

pop() — Remove by index (and return the value)

fruits = ["apple", "banana", "cherry"]
removed = fruits.pop(1)   # removes index 1
print(removed)   # banana
print(fruits)    # ['apple', 'cherry']

Without an index, pop() removes and returns the last item — useful for treating a list like a stack.

fruits = ["apple", "banana", "cherry"]
last = fruits.pop()
print(last)    # cherry
print(fruits)  # ['apple', 'banana']

del — Remove by index or slice

fruits = ["apple", "banana", "cherry", "date"]
del fruits[1]
print(fruits)   # ['apple', 'cherry', 'date']

del fruits[0:2]
print(fruits)   # ['date']

clear() — Remove everything

fruits = ["apple", "banana", "cherry"]
fruits.clear()
print(fruits)   # []

Your turn: Create a list of 5 items. Remove the first item using pop(0). Remove a specific item by value using remove(). Print the list after each operation.

Useful Functions for Lists

len() — How many items?

fruits = ["apple", "banana", "cherry"]
print(len(fruits))   # 3

sum(), min(), max() — Math on a list of numbers

scores = [88, 72, 95, 61, 84]

print(sum(scores))   # 400
print(min(scores))   # 61
print(max(scores))   # 95
print(sum(scores) / len(scores))   # 80.0  (average)

sorted() — Sort without changing the original

scores = [88, 72, 95, 61, 84]
sorted_scores = sorted(scores)
print(sorted_scores)   # [61, 72, 84, 88, 95]
print(scores)          # [88, 72, 95, 61, 84]  (unchanged)

To sort in descending order:

print(sorted(scores, reverse=True))   # [95, 88, 84, 72, 61]

.sort() — Sort in place (changes the original)

scores = [88, 72, 95, 61, 84]
scores.sort()
print(scores)   # [61, 72, 84, 88, 95]

reversed() — Iterate in reverse order

fruits = ["apple", "banana", "cherry"]
for fruit in reversed(fruits):
    print(fruit)

Output:

cherry
banana
apple

More List Methods

count() — How many times does a value appear?

numbers = [1, 2, 3, 2, 4, 2, 5]
print(numbers.count(2))   # 3

index() — What position is this value at?

fruits = ["apple", "banana", "cherry"]
print(fruits.index("banana"))   # 1

Raises ValueError if the item isn't in the list.

copy() — Make a shallow copy

original = [1, 2, 3]
copy = original.copy()
copy.append(4)

print(original)   # [1, 2, 3]  (unchanged)
print(copy)       # [1, 2, 3, 4]

Why does this matter? Because if you do copy = original, you don't get a copy — both variables point to the same list. Changing one changes the other. copy() gives you an independent duplicate.

original = [1, 2, 3]
alias = original      # NOT a copy — same list
alias.append(99)

print(original)   # [1, 2, 3, 99]  (original was changed!)

This surprises almost every beginner at least once. Now you know.

Checking if an Item is in a List

Use the in keyword.

fruits = ["apple", "banana", "cherry"]

print("banana" in fruits)    # True
print("grape" in fruits)     # False
print("grape" not in fruits) # True

Combine with if for practical use:

shopping_cart = ["bread", "milk", "eggs"]

item = input("Enter an item to check: ")

if item in shopping_cart:
    print(f"{item} is in your cart.")
else:
    print(f"{item} is not in your cart.")

Looping Over a List

Loop over values directly

names = ["Alice", "Bob", "Carlos"]

for name in names:
    print(f"Hello, {name}!")

Output:

Hello, Alice!
Hello, Bob!
Hello, Carlos!

Loop with index using enumerate()

When you need both the index and the value, use enumerate(). It gives you both on each iteration.

fruits = ["apple", "banana", "cherry"]

for index, fruit in enumerate(fruits):
    print(f"{index}: {fruit}")

Output:

0: apple
1: banana
2: cherry

Start the count from 1 instead of 0:

for index, fruit in enumerate(fruits, start=1):
    print(f"{index}. {fruit}")

Output:

1. apple
2. banana
3. cherry

This is cleaner than using range(len(fruits)) and indexing manually.

Nested Lists — Lists Inside Lists

A list can contain other lists. This is how you represent a grid, a table, or a matrix.

grid = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
]

print(grid[0])      # [1, 2, 3]
print(grid[1][2])   # 6  (row 1, column 2)

Loop over a nested list with nested loops:

for row in grid:
    for value in row:
        print(value, end="  ")
    print()

Output:

1  2  3  
4  5  6  
7  8  9  

Real use case: a tic-tac-toe board, a seating chart, a spreadsheet.

List Comprehensions — A Powerful Shortcut

A list comprehension creates a new list by applying an expression to every item in a sequence. It's one of Python's most elegant features.

The pattern is: [expression for item in sequence]

# The long way
squares = []
for n in range(1, 6):
    squares.append(n ** 2)
print(squares)   # [1, 4, 9, 16, 25]

# The list comprehension way
squares = [n ** 2 for n in range(1, 6)]
print(squares)   # [1, 4, 9, 16, 25]

Same result. Half the lines.

You can add a condition to filter items:

# Only even squares
even_squares = [n ** 2 for n in range(1, 11) if n % 2 == 0]
print(even_squares)   # [4, 16, 36, 64, 100]
# Uppercase only the long names
names = ["Alice", "Bob", "Christopher", "Di", "Alejandro"]
long_names = [name.upper() for name in names if len(name) > 4]
print(long_names)   # ['ALICE', 'CHRISTOPHER', 'ALEJANDRO']

List comprehensions are covered in full depth in Chapter 14. For now, just know they exist and start recognizing the pattern.

Your turn: Use a list comprehension to create a list of all numbers from 1 to 50 that are divisible by 3. Print the result.

Putting It All Together: Student Grade Tracker

Here's a program that uses almost everything from this chapter.

# Student grade tracker
students = []
grades = []

print("Enter 5 student names and their grades.")

for i in range(5):
    name = input(f"Student {i + 1} name: ")
    grade = float(input(f"Grade for {name}: "))
    students.append(name)
    grades.append(grade)

print("\n--- Results ---")

for i, name in enumerate(students):
    status = "Pass" if grades[i] >= 50 else "Fail"
    print(f"{name}: {grades[i]}{status}")

print(f"\nHighest grade: {max(grades)}")
print(f"Lowest grade:  {min(grades)}")
print(f"Average grade: {round(sum(grades) / len(grades), 1)}")
print(f"Top student:   {students[grades.index(max(grades))]}")

Sample output:

--- Results ---
Alice: 88.0  Pass
Bob: 45.0  Fail
Carlos: 92.0  Pass
Diana: 71.0  Pass
Eve: 55.0  Pass

Highest grade: 92.0
Lowest grade:  45.0
Average grade: 70.2
Top student:   Carlos

grades.index(max(grades)) finds the position of the highest grade, then uses that same position to find the student name. That's two list operations working together.

What You Learned in This Chapter

  • A list stores many values in one variable, in order, using [].
  • Indexing: list[0] is the first item. list[-1] is the last. Counting starts at 0.
  • Slicing: list[start:stop] extracts a portion. list[::-1] reverses.
  • Lists are mutable — you can change items by assigning to an index.
  • Adding items: append() (end), insert() (position), extend() (add list), + (new list).
  • Removing items: remove() (by value), pop() (by index), del, clear().
  • Useful functions: len(), sum(), min(), max(), sorted(), reversed().
  • Useful methods: sort(), count(), index(), copy().
  • Check membership with in and not in.
  • Loop with for item in list or for index, item in enumerate(list).
  • Nested lists represent grids and tables.
  • List comprehensions create lists in one readable line.
  • copy = original is not a copy — use .copy() to get a real independent duplicate.

What's Next?

You can now store and manipulate collections of data. The next step is organizing your code.

In Chapter 7 you'll learn about functions — a way to give a name to a block of code and reuse it wherever you need it. Functions turn a 200-line program into something clean and readable. They also make it possible to break a big problem into small, solvable pieces — which is exactly how professional programmers think.

Your turn: Write a program that asks the user to enter numbers until they type -1 (use a while loop). Store each number (except -1) in a list. After they stop, print: the list, the total, the average, the highest, and the lowest. If they entered no numbers, print "No numbers entered."

© 2026 Abhilash Sahoo. Python: Zero to Hero.