š 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(), andpop() - 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.
š ļø 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]]
[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:
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:
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) ā instant | Direct access by index |
list.append(x) | O(1) ā instant | Very fast |
list.pop() | O(1) ā instant | Fast (from end) |
list.insert(0, x) | O(n) ā slower | Must shift all items |
list.pop(0) | O(n) ā slower | Must shift all items |
x in list | O(n) ā slower | Must 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")
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]
# 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 collection | List | Maintains insertion order |
| Unique items only | Set | Automatically removes duplicates |
| Key-value pairs | Dictionary | Fast lookups by key (next lesson!) |
| Immutable sequence | Tuple | Can't be changed, hashable |
| Queue (FIFO) | deque | Efficient 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:
- Ask the user to enter scores one at a time (type "done" to finish)
- Store them in a list
- Display the average, highest, and lowest scores
- 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:
- Create a menu with options: add, remove, shuffle, display, quit
- Use
import randomandrandom.shuffle()for shuffling - 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:
- Create a list of secret words
- Pick one at random
- Give hints: number of letters, first letter
- 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!