Skip to main content

šŸ“‹ Lesson 7: Lists — Python's Super Memory

Imagine you have a backpack where you can store multiple items. That's what a list is in Python — a container that can hold multiple values, all with one name!

šŸŽÆ Learning Objectives

By the end of this lesson, you will be able to:

  • Create lists and access items by index (positive and negative)
  • Use slicing to extract portions of a list
  • Modify lists with methods like append(), insert(), remove(), and pop()
  • Combine lists and loops for powerful data processing
  • Write list comprehensions for concise, readable code

Estimated Time: 50–65 minutes

Project: Build a Shopping List Manager with add, remove, view, and clear features

In This Lesson

šŸ“¦ What Are Lists?

So far, each variable you've created holds one value — one number, one string, one boolean. But what if you need to store a whole collection of things? Five test scores, a shopping list, a roster of player names?

A list is an ordered collection of items stored in a single variable. Each item has a numbered position (called an index) starting from 0.

šŸ“‹ A List Is Like a Row of Labeled Boxes [0] [1] [2] [3] [4] "apple" "banana" "cherry" "date" "elder." fruits = ["apple", "banana", "cherry", "date", "elderberry"] fruits[0] → "apple" fruits[2] → "cherry" fruits[-1] → "elderberry" len(fruits) → 5

šŸ› ļø Creating Lists

Lists are created with square brackets []. They can hold any data type — and even mix types together:

# Empty list
shopping_cart = []

# List with items
fruits = ["apple", "banana", "cherry"]
numbers = [1, 2, 3, 4, 5]
mixed = ["hello", 42, 3.14, True]  # Lists can mix types!

# Lists can contain other lists!
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
graph LR A["šŸ“‹ List Types"] --> B["šŸ”¢ Numbers
[1, 2, 3]"] A --> C["šŸ“ Strings
['a', 'b', 'c']"] A --> D["šŸŽØ Mixed
['hi', 42, True]"] A --> E["šŸ“¦ Nested
[[1,2], [3,4]]"]

šŸ” Accessing List Items

Each item in a list has an address called an index. Python uses two numbering systems:

graph LR A["šŸ”¢ List Indexing"] --> B["āž”ļø Positive: 0, 1, 2...
Start from beginning"] A --> C["ā¬…ļø Negative: -1, -2, -3...
Start from end"]
colors = ["red", "green", "blue", "yellow", "purple"]

# Positive indexing (from start)
print(colors[0])    # "red" (first item)
print(colors[2])    # "blue" (third item)

# Negative indexing (from end)
print(colors[-1])   # "purple" (last item)
print(colors[-2])   # "yellow" (second to last)

šŸ“– Key Concept

Zero-based indexing: The first item is always at index 0, not 1. This is one of the most common sources of confusion for beginners!

āœ‚ļø Slicing — Grabbing Multiple Items

Slicing lets you extract a portion of a list using the syntax [start:stop]. The stop index is not included.

colors = ["red", "green", "blue", "yellow", "purple"]

# Basic slicing
print(colors[1:4])  # ["green", "blue", "yellow"]
print(colors[:3])   # ["red", "green", "blue"] (first 3)
print(colors[2:])   # ["blue", "yellow", "purple"] (from index 2 on)

šŸ†• Advanced Slicing with Step

The full syntax is [start:stop:step] — the step parameter is powerful!

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

# Every 2nd item
print(numbers[::2])      # [0, 2, 4, 6, 8]

# Every 3rd item starting from index 1
print(numbers[1::3])     # [1, 4, 7]

# Reverse the list
print(numbers[::-1])     # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

# Reverse every 2nd item
print(numbers[::-2])     # [9, 7, 5, 3, 1]

āš ļø Copying Lists — Why It Matters

original = [1, 2, 3]
copy1 = original[:]      # Full slice creates a copy āœ…
copy2 = original.copy()  # Using copy method āœ…
copy3 = list(original)   # Using list constructor āœ…

# This does NOT copy — just creates another reference!
wrong_copy = original    # āŒ
wrong_copy.append(4)
print(original)          # [1, 2, 3, 4] — original changed too!

šŸ”§ Modifying Lists

Lists are mutable — you can change them after creation. Python gives you a toolbox of built-in methods:

🧰 Common List Operations .append() Add to end .insert() Add at position .remove() Remove by value .pop() Remove & return .extend() Add multiple items .sort() Order items All of these modify the original list in place

List Methods in Action

# Starting with a todo list
todo = ["wake up", "coffee", "code"]
print(f"Original: {todo}")

# Adding items
todo.append("lunch")                    # Add to end
print(f"After append: {todo}")

todo.insert(2, "check email")           # Insert at position 2
print(f"After insert: {todo}")

# Removing items
todo.remove("coffee")                   # Remove specific item
print(f"After remove: {todo}")

completed = todo.pop(0)                 # Remove and return first item
print(f"Completed: {completed}")
print(f"After pop: {todo}")

# Other useful methods
todo.reverse()                          # Reverse the order
print(f"Reversed: {todo}")

print(f"'code' is at index: {todo.index('code')}")
print(f"Number of 'code' items: {todo.count('code')}")

Output:

Original: ['wake up', 'coffee', 'code']
After append: ['wake up', 'coffee', 'code', 'lunch']
After insert: ['wake up', 'coffee', 'check email', 'code', 'lunch']
After remove: ['wake up', 'check email', 'code', 'lunch']
Completed: wake up
After pop: ['check email', 'code', 'lunch']
Reversed: ['lunch', 'code', 'check email']
'code' is at index: 1
Number of 'code' items: 1

ā±ļø Performance at a Glance

Operation Speed Notes
list[i]O(1) — instantDirect access by index
list.append(x)O(1) — instantVery fast
list.pop()O(1) — instantFast (from end)
list.insert(0, x)O(n) — slowerMust shift all items
list.pop(0)O(n) — slowerMust shift all items
x in listO(n) — slowerMust check each item
list.sort()O(n log n)Efficient sorting

āœ… Pro Tip

If you need frequent insertions and deletions at both ends, look into collections.deque — it's built for that!

šŸ”„ Lists and Loops — Perfect Partners

Lists and loops work together beautifully. You can process every item in a list automatically:

# Processing each item
scores = [85, 92, 78, 95, 88]

# Calculate average
total = 0
for score in scores:
    total += score
average = total / len(scores)
print(f"Average score: {average:.1f}")

# Find highest score
highest = scores[0]
for score in scores:
    if score > highest:
        highest = score
print(f"Highest score: {highest}")

Output:

Average score: 87.6
Highest score: 95

Looping with Index — enumerate()

# The long way — range(len(...))
print("Score Report:")
for i in range(len(scores)):
    print(f"  Test {i+1}: {scores[i]} points")

# The Pythonic way — enumerate!
print("\nUsing enumerate:")
for i, score in enumerate(scores):
    print(f"  Test {i+1}: {score} points")
graph TD A["šŸ”„ Looping Strategies"] --> B["for item in list
When you only need values"] A --> C["for i in range(len(list))
When you need the index"] A --> D["for i, item in enumerate(list)
✨ Best of both worlds!"]

✨ List Comprehensions — Python Magic

List comprehensions are a concise way to create new lists. They pack a loop (and optional condition) into a single line:

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

# List comprehension — same result, one line!
squares = [i ** 2 for i in range(1, 6)]
print(squares)  # [1, 4, 9, 16, 25]
graph LR A["[expression"] --> B["for item in iterable"] B --> C["if condition]"] style A fill:#eff6ff,stroke:#3b82f6 style B fill:#f0fdf4,stroke:#22c55e style C fill:#fef9c3,stroke:#eab308
# With conditions — filter as you build
evens = [x for x in range(20) if x % 2 == 0]
print(evens)  # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

# Transform strings
fruits = ["apple", "banana", "cherry"]
uppercase = [fruit.upper() for fruit in fruits]
print(uppercase)  # ['APPLE', 'BANANA', 'CHERRY']

# Practical example: temperature conversion
temperatures_f = [32, 68, 75, 82, 90]
temperatures_c = [(f - 32) * 5/9 for f in temperatures_f]
print(f"Celsius: {[f'{c:.1f}' for c in temperatures_c]}")

Output:

Celsius: ['0.0', '20.0', '23.9', '27.8', '32.2']

šŸ”€ Sorting — sort() vs sorted()

Python gives you two ways to sort. They look similar but behave differently:

# .sort() — modifies the list IN PLACE, returns None
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
numbers.sort()
print(numbers)  # [1, 1, 2, 3, 4, 5, 6, 9] — original changed

# sorted() — returns a NEW sorted list, original untouched
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
sorted_nums = sorted(numbers)
print(numbers)      # [3, 1, 4, 1, 5, 9, 2, 6] — original unchanged
print(sorted_nums)  # [1, 1, 2, 3, 4, 5, 6, 9] — new list
# Both support reverse and key parameters
words = ["banana", "pie", "Washington", "book"]
words.sort(key=len, reverse=True)
print(words)  # ['Washington', 'banana', 'book', 'pie']

# sorted() works on any iterable
print(sorted("python"))          # ['h', 'n', 'o', 'p', 't', 'y']
print(sorted({3, 1, 4, 1, 5}))  # [1, 3, 4, 5]

# Custom sorting with lambda
students = [("Alice", 85), ("Bob", 75), ("Carol", 95)]
students.sort(key=lambda x: x[1])
print(students)  # [('Bob', 75), ('Alice', 85), ('Carol', 95)]

šŸ“– Quick Rule

Use .sort() when you don't need the original order anymore. Use sorted() when you want to keep the original list intact.

šŸ“ Lists vs Strings

Strings behave a lot like lists of characters — but there's one critical difference:

# Similarities — indexing, slicing, looping
text = "Python"
print(text[0])     # 'P'
print(text[-1])    # 'n'
print(text[1:4])   # 'yth'

# The big difference: mutability
my_list = ['P', 'y', 't', 'h', 'o', 'n']
my_list[0] = 'J'   # Works! Lists are mutable āœ…
print(my_list)      # ['J', 'y', 't', 'h', 'o', 'n']

# text[0] = 'J'     # TypeError! Strings are immutable āŒ

Converting Between Strings and Lists

# Split string → list
sentence = "Python is awesome"
words = sentence.split()
print(words)  # ['Python', 'is', 'awesome']

# Join list → string
joined = " ".join(words)
print(joined)  # "Python is awesome"

# Characters to list and back
chars = list("Hello")   # ['H', 'e', 'l', 'l', 'o']
word = "".join(chars)    # "Hello"

🧩 Common Patterns and Mistakes

Useful Patterns

# Pattern 1: Filtering
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
odds = [num for num in numbers if num % 2 != 0]
print(f"Odd numbers: {odds}")

# Pattern 2: Transformation
words = ["hello", "world", "python"]
lengths = [len(word) for word in words]
print(f"Word lengths: {lengths}")

# Pattern 3: Finding items
inventory = ["hammer", "nails", "screwdriver", "wrench"]
item = "screwdriver"
if item in inventory:
    print(f"Found {item} at position {inventory.index(item)}")

# Pattern 4: Removing duplicates
numbers = [1, 2, 2, 3, 3, 3, 4, 5, 5]
unique = list(set(numbers))
print(f"Unique: {unique}")

āš ļø Mistake: Index Out of Range

# Wrong
fruits = ["apple", "banana"]
# print(fruits[2])  # IndexError! Only indices 0 and 1

# Right — check length first
if len(fruits) > 2:
    print(fruits[2])
else:
    print("Not enough items")

āš ļø Mistake: Modifying a List While Looping

# WRONG — modifying the list you're looping through
numbers = [1, 2, 3, 4, 5]
for num in numbers:
    if num % 2 == 0:
        numbers.remove(num)  # Dangerous! Skips items

# RIGHT — use a list comprehension instead
numbers = [1, 2, 3, 4, 5]
numbers = [num for num in numbers if num % 2 != 0]
print(numbers)  # [1, 3, 5]

šŸ¤” When to Use Lists vs Other Structures

Use Case Best Choice Why
Ordered collectionListMaintains insertion order
Unique items onlySetAutomatically removes duplicates
Key-value pairsDictionaryFast lookups by key (next lesson!)
Immutable sequenceTupleCan't be changed, hashable
Queue (FIFO)dequeEfficient add/remove from both ends

šŸ›’ Project: Shopping List Manager

Let's put it all together! This project uses lists, loops, input, and if-statements — everything you've learned so far:

# Shopping List Manager
shopping_list = []

print("šŸ›’ Shopping List Manager šŸ›’")
print("-" * 30)

while True:
    print("\nCurrent list:", shopping_list if shopping_list else "[Empty]")
    print("\nOptions:")
    print("1. Add item")
    print("2. Remove item")
    print("3. View list")
    print("4. Clear list")
    print("5. Exit")

    choice = input("\nEnter choice (1-5): ")

    if choice == "1":
        item = input("Enter item to add: ")
        shopping_list.append(item)
        print(f"Added '{item}' to list!")

    elif choice == "2":
        if shopping_list:
            print("Current items:")
            for i, item in enumerate(shopping_list):
                print(f"  {i+1}. {item}")
            try:
                index = int(input("Enter number to remove: ")) - 1
                removed = shopping_list.pop(index)
                print(f"Removed '{removed}'")
            except:
                print("Invalid selection!")
        else:
            print("List is empty!")

    elif choice == "3":
        if shopping_list:
            print("\nšŸ“‹ Shopping List:")
            for i, item in enumerate(shopping_list, 1):
                print(f"  {i}. {item}")
        else:
            print("List is empty!")

    elif choice == "4":
        shopping_list.clear()
        print("List cleared!")

    elif choice == "5":
        print("Happy shopping! šŸ‘‹")
        break

    else:
        print("Invalid choice!")

šŸ‹ļø Practice Exercises

šŸ‹ļø Exercise 1: Grade Calculator

Objective: Create a program that lets the user enter multiple test scores, calculates the average, highest, and lowest, then drops the lowest score and recalculates the average.

Instructions:

  1. Ask the user to enter scores one at a time (type "done" to finish)
  2. Store them in a list
  3. Display the average, highest, and lowest scores
  4. Drop the lowest score and show the new average
šŸ’” Hint

Use min() and max() for the highest/lowest values. To drop the lowest, use .remove(min(scores)) on a copy of the list.

āœ… Solution
scores = []

print("šŸ“ Grade Calculator")
print("Enter scores one at a time. Type 'done' when finished.\n")

while True:
    entry = input("Enter a score: ")
    if entry.lower() == "done":
        break
    try:
        scores.append(float(entry))
    except ValueError:
        print("Please enter a number or 'done'.")

if scores:
    average = sum(scores) / len(scores)
    print(f"\n--- Results ({len(scores)} scores) ---")
    print(f"Average:  {average:.1f}")
    print(f"Highest:  {max(scores)}")
    print(f"Lowest:   {min(scores)}")

    # Drop lowest and recalculate
    adjusted = scores.copy()
    adjusted.remove(min(adjusted))
    new_avg = sum(adjusted) / len(adjusted)
    print(f"\nAfter dropping lowest ({min(scores)}):")
    print(f"New average: {new_avg:.1f}")
else:
    print("No scores entered.")

šŸ‹ļø Exercise 2: Playlist Manager

Objective: Build a music playlist program that maintains a list of songs, allows adding, removing, and reordering songs, and can shuffle the playlist.

Instructions:

  1. Create a menu with options: add, remove, shuffle, display, quit
  2. Use import random and random.shuffle() for shuffling
  3. Display the current song and next/previous
šŸ’” Hint

Track the "now playing" index as a variable. Use random.shuffle(playlist) to randomize. Remember to handle edge cases like an empty playlist.

āœ… Solution
import random

playlist = []
current = 0

print("šŸŽµ Playlist Manager šŸŽµ")

while True:
    print(f"\nPlaylist: {playlist if playlist else '[Empty]'}")
    if playlist:
        print(f"Now playing: {playlist[current]}")
    print("\n1. Add song  2. Remove song  3. Shuffle")
    print("4. Next  5. Previous  6. Quit")

    choice = input("Choice: ")

    if choice == "1":
        song = input("Song name: ")
        playlist.append(song)
        print(f"Added '{song}'")

    elif choice == "2" and playlist:
        for i, s in enumerate(playlist, 1):
            print(f"  {i}. {s}")
        try:
            idx = int(input("Number to remove: ")) - 1
            removed = playlist.pop(idx)
            current = min(current, len(playlist) - 1)
            print(f"Removed '{removed}'")
        except:
            print("Invalid!")

    elif choice == "3" and playlist:
        random.shuffle(playlist)
        current = 0
        print("Playlist shuffled!")

    elif choice == "4" and playlist:
        current = (current + 1) % len(playlist)

    elif choice == "5" and playlist:
        current = (current - 1) % len(playlist)

    elif choice == "6":
        print("Rock on! šŸŽø")
        break

šŸ‹ļø Exercise 3: Word Guessing Game

Objective: Create a game with a list of secret words that gives hints and reveals the word letter by letter.

Instructions:

  1. Create a list of secret words
  2. Pick one at random
  3. Give hints: number of letters, first letter
  4. Track guessed letters and reveal matches
šŸ’” Hint

Use a list of underscores (e.g., ["_"] * len(word)) to track revealed letters. Use random.choice() to pick a word.

āœ… Solution
import random

words = ["python", "coding", "variable", "function", "loops"]
secret = random.choice(words)
revealed = ["_"] * len(secret)
guessed = []
attempts = 6

print("šŸ”® Word Guessing Game!")
print(f"The word has {len(secret)} letters.")
print(f"Hint: it starts with '{secret[0]}'")
revealed[0] = secret[0]

while attempts > 0 and "_" in revealed:
    print(f"\nWord: {' '.join(revealed)}")
    print(f"Guessed: {', '.join(guessed) if guessed else 'none'}")
    print(f"Attempts left: {attempts}")

    guess = input("Guess a letter: ").lower()

    if len(guess) != 1 or not guess.isalpha():
        print("Enter a single letter.")
        continue
    if guess in guessed:
        print("Already guessed!")
        continue

    guessed.append(guess)

    if guess in secret:
        for i, letter in enumerate(secret):
            if letter == guess:
                revealed[i] = guess
        print(f"āœ… '{guess}' is in the word!")
    else:
        attempts -= 1
        print(f"āŒ '{guess}' is not in the word.")

if "_" not in revealed:
    print(f"\nšŸŽ‰ You got it! The word was '{secret}'!")
else:
    print(f"\n😢 Out of attempts. The word was '{secret}'.")

šŸŽÆ Quick Quiz

Question 1: What does fruits[āˆ’1] return if fruits = ["apple", "banana", "cherry"]?

Question 2: What is the difference between .append() and .extend()?

Question 3: What does [x * 2 for x in range(4)] produce?

šŸ“š Summary

šŸŽ‰ Key Takeaways

  • Lists store multiple values in order, accessed by index starting at 0
  • Lists are mutable — you can add, remove, and change items
  • Slicing ([start:stop:step]) lets you grab portions of a list
  • Built-in methods like .append(), .pop(), and .sort() make list manipulation easy
  • Lists and loops are best friends — enumerate() gives you both index and value
  • List comprehensions offer powerful, readable one-liners
  • Always check bounds and never modify a list while looping over it

šŸ“Œ Real-World Applications

Lists are everywhere in real Python code: storing survey responses and measurements (data analysis), tracking inventory items and high scores (games), managing user lists and product catalogs (web development), queuing tasks and file lists (automation), and holding training data and predictions (machine learning).

šŸš€ What's Next?

Now that you can store multiple items, we'll explore dictionaries — Python's way of storing data with labels. Think of them as lists where items have names instead of numbers!

šŸŽ‰ Congratulations!

You've started Module 4: Data Structures! Lists are one of Python's most versatile features — master them and you'll find yourself using them everywhere. They're like Swiss Army knives for data storage!