Storing, reading, changing, searching, and processing collections of values — the workhorse data structure of practical Python programming
Every program you have written so far has worked with one value at a time. A variable holds one number. A string holds one piece of text. But real problems almost never involve a single value. A class has thirty students. A quiz has twenty questions. A store has thousands of items. A weather station records a temperature every hour for a year.
When a problem involves a collection of values, you need a data structure — a way of organizing multiple pieces of information so your program can work with all of them together. Python's most important and versatile data structure is the list.
This chapter teaches you how lists work, how to create them, how to read and change their contents, how to loop through them, how to search them, and how to apply the patterns — totals, counts, maximums, minimums — that appear constantly in real programs. Lists are not just a new syntax rule. They change what kinds of problems you can solve. By the end of this chapter you should be able to create lists, access individual elements, update values, add and remove items, iterate through a list with a loop, search for values, slice a list, use common list methods, and write list-processing functions. You will also build a small to-do list manager as the chapter mini-project.
By the end of this chapter, you should be able to:
len() function.append() and insert(), and remove elements with remove() and pop().for loop, including index-based iteration for modifying elements.in, and search lists using index() and count().sort(), sorted(), reverse(), extend(), clear(), and copy().IndexError and the append-assignment mistake.len(), the valid index range, IndexErrorappend(), remove(), pop(), insert(), the append-assignment mistakefor item in list pattern; index-based iteration; modifying elements during a loop; the loop-variable-copy trapin and not in; index(); count(); searching with a loop and breaklist[start:stop], omitted bounds, step values, copying with a full slicesort(), sorted(), reverse(), extend(), clear(), copy(), and a method summary tableImagine you are writing a program that reads five test scores and computes their average. You already know how to do this with five separate variables:
score1 = 88 score2 = 74 score3 = 91 score4 = 65 score5 = 83 total = score1 + score2 + score3 + score4 + score5 average = total / 5 print(average)
This works. But what if there are fifty scores instead of five? You would need to write fifty variable names and then add them all by hand. And what if the number of scores is not known until the program runs — for example, because the user enters them one at a time? Separate variables cannot solve that problem at all.
A list solves both problems. Instead of five separate variables, you have one variable that holds all five scores together:
scores = [88, 74, 91, 65, 83]
You can loop over the list no matter how long it is. You can add new items to it while the program runs. You can pass the whole collection into a function with a single argument. You can sort it, search it, and measure it.
Lists appear everywhere in real Python programs:
Once you understand lists, you will find yourself reaching for them constantly. They are the workhorse of practical Python programming.
A list is written as a sequence of values separated by commas and enclosed in square brackets.
scores = [88, 74, 91, 65, 83] names = ["Alice", "Bob", "Carlos", "Diana"] temperatures = [98.6, 99.1, 97.8, 100.2] flags = [True, False, True, True]
Each value inside a list is called an element or item. The elements are stored in order, from left to right, and that order is preserved. Python does not rearrange your list unless you ask it to.
A list can contain any type of value: integers, floats, strings, Booleans, or even other lists. A list can also mix types, though it is usually cleaner to keep one type per list:
mixed = [42, "hello", 3.14, True] # valid but unusual
You can create an empty list with a pair of empty brackets:
items = []
An empty list is useful when you plan to add elements to the list as the program runs.
You can also use a variable to build a list:
first = 10
second = 20
third = 30
numbers = [first, second, third]
print(numbers) # [10, 20, 30]
Python evaluates each expression inside the brackets and uses the resulting values.
Before moving on, make sure you can answer these without looking back: What characters mark the beginning and end of a list? What is each value inside a list called? What does an empty list look like?
Every element in a list has a position called its index. Python numbers list positions starting at zero, not one. This is called zero-based indexing and it appears in every modern programming language.
colors = ["red", "green", "blue", "yellow"]
# 0 1 2 3
The element "red" is at index 0. The element "green" is at index 1. The element "blue" is at index 2. The element "yellow" is at index 3.
To access a single element, write the list name followed by the index in square brackets:
colors = ["red", "green", "blue", "yellow"] print(colors[0]) # red print(colors[1]) # green print(colors[3]) # yellow
Negative indexes count from the end of the list. The index -1 refers to the last element, -2 to the second-to-last, and so on:
print(colors[-1]) # yellow print(colors[-2]) # blue
Negative indexes are useful when you need the last element and you do not know how long the list is.
The len() function returns the number of elements in a list:
colors = ["red", "green", "blue", "yellow"]
print(len(colors)) # 4
For a list with four elements, the valid indexes are 0, 1, 2, and 3. In general, the valid indexes for a list named items run from 0 to len(items) - 1. Trying to access an index outside this range produces an IndexError:
print(colors[4]) # IndexError: list index out of range
This is one of the most common errors beginners make. Always remember that the last valid index is len(list_name) - 1, not len(list_name).
scores = [88, 74, 91, 65, 83]
print("Number of scores:", len(scores))
print("First score:", scores[0])
print("Last score:", scores[-1])
print("Second score:", scores[1])
Number of scores: 5 First score: 88 Last score: 83 Second score: 74
Once a list exists, you can read any element using its index, as shown in the previous section. You can also change a specific element by assigning a new value to its index position:
scores = [88, 74, 91, 65, 83] print(scores[1]) # 74 scores[1] = 80 print(scores[1]) # 80 print(scores) # [88, 80, 91, 65, 83]
The assignment scores[1] = 80 replaces the value at index 1 with the new value 80. The rest of the list is unchanged.
Lists in Python are mutable, which means their contents can be changed after they are created. This is different from strings, which you learned in Chapter 16 are immutable — once a string is created, you cannot change individual characters inside it.
A program records five temperatures but the third reading was entered incorrectly. The temperature should be 98.9, not 98.0.
temps = [98.6, 99.1, 98.0, 100.2, 97.8]
print("Before correction:", temps)
temps[2] = 98.9
print("After correction:", temps)
Before correction: [98.6, 99.1, 98.0, 100.2, 97.8] After correction: [98.6, 99.1, 98.9, 100.2, 97.8]
You can also use a list element in an expression and then store the result back:
scores = [88, 74, 91, 65, 83] scores[0] = scores[0] + 5 # add 5 bonus points to first score print(scores[0]) # 93
Trace the following code and write down the value of numbers after each line, before reading the answer below.
numbers = [10, 20, 30, 40] numbers[0] = 15 numbers[3] = numbers[3] - 10 numbers[1] = numbers[0] + numbers[2] print(numbers)
| After Line | Value of numbers |
|---|---|
| Line 1 | [10, 20, 30, 40] |
| Line 2 | [15, 20, 30, 40] |
| Line 3 | [15, 20, 30, 30] |
| Line 4 | [15, 45, 30, 30] |
[15, 45, 30, 30]
Lists can grow and shrink while a program runs. The two most basic operations are adding an item to the end of a list and removing an item from a list.
The append() method adds a single element to the end of a list:
fruits = ["apple", "banana"]
fruits.append("cherry")
print(fruits) # ['apple', 'banana', 'cherry']
fruits.append("date")
print(fruits) # ['apple', 'banana', 'cherry', 'date']
append() always adds to the end. The list grows by one element each time you call it.
This is the standard way to build a list during a loop — you start with an empty list and append each new item as you go:
collected = []
collected.append(10)
collected.append(20)
collected.append(30)
print(collected) # [10, 20, 30]
The remove() method removes the first occurrence of a specific value:
fruits = ["apple", "banana", "cherry", "banana"]
fruits.remove("banana")
print(fruits) # ['apple', 'cherry', 'banana']
Notice that only the first "banana" was removed. If you call remove() with a value that is not in the list, Python raises a ValueError:
fruits.remove("grape") # ValueError: list.remove(x): x not in list
The pop() method removes and returns the element at a given index. If you call it with no argument, it removes and returns the last element:
fruits = ["apple", "banana", "cherry"] removed = fruits.pop() print(removed) # cherry print(fruits) # ['apple', 'banana'] removed = fruits.pop(0) print(removed) # apple print(fruits) # ['banana']
pop() is useful when you need to process items one at a time by removing them from the list.
The insert() method adds an element at a specific position:
fruits = ["apple", "cherry"]
fruits.insert(1, "banana")
print(fruits) # ['apple', 'banana', 'cherry']
insert(1, "banana") means: place "banana" at index 1, shifting everything else to the right.
A frequent error is accidentally replacing a list with None by misusing append().
# Incorrect numbers = [1, 2, 3] numbers = numbers.append(4) # numbers is now None! print(numbers) # None
append() modifies the list in place and returns None. The assignment numbers = numbers.append(4) stores None back into numbers. The correct approach is:
# Correct numbers = [1, 2, 3] numbers.append(4) print(numbers) # [1, 2, 3, 4]
The most important thing you can do with a list is loop through all of its elements. A for loop is designed exactly for this:
scores = [88, 74, 91, 65, 83]
for score in scores:
print(score)
88 74 91 65 83
The loop variable score takes on each element's value in order, from index 0 to the last index. The loop body runs once for each element.
You can do anything inside the loop body that you could do with a regular value:
scores = [88, 74, 91, 65, 83]
for score in scores:
if score >= 90:
print(score, "— Excellent")
elif score >= 80:
print(score, "— Good")
else:
print(score, "— Needs work")
88 — Good 74 — Needs work 91 — Excellent 65 — Needs work 83 — Good
Iterating with an index. Sometimes you need both the element and its index. Use range(len(...)):
names = ["Alice", "Bob", "Carlos"]
for i in range(len(names)):
print(i, names[i])
0 Alice 1 Bob 2 Carlos
This is useful when you need to modify elements inside the loop (you must use the index to assign a new value) or when you need to compare adjacent elements.
Modifying elements during a loop. If you need to change every element, use index-based iteration:
scores = [88, 74, 91, 65, 83] # Add 5 bonus points to every score for i in range(len(scores)): scores[i] = scores[i] + 5 print(scores) # [93, 79, 96, 70, 88]
Do not use for item in list and then try to change item to change the list. This does not work.
scores = [88, 74, 91, 65, 83]
for score in scores:
score = score + 5 # changes the loop variable, not the list
print(scores) # [88, 74, 91, 65, 83] — unchanged!
The variable score is a copy of the element's value. Changing score does not change the list. To change the list, you must assign back using the index.
inThe in operator tests whether a value exists anywhere in a list:
fruits = ["apple", "banana", "cherry"]
print("banana" in fruits) # True
print("grape" in fruits) # False
Use not in to test the opposite:
print("grape" not in fruits) # True
in is commonly used inside if statements:
allowed = ["Alice", "Bob", "Carlos"]
name = input("Enter your name: ")
if name in allowed:
print("Welcome,", name)
else:
print("Access denied.")
The index() method returns the index of the first occurrence of a value:
fruits = ["apple", "banana", "cherry", "banana"]
print(fruits.index("banana")) # 1
print(fruits.index("cherry")) # 2
If the value is not in the list, index() raises a ValueError. To search safely, check with in first:
if "grape" in fruits:
print(fruits.index("grape"))
else:
print("Not found")
The count() method returns the number of times a value appears:
votes = ["yes", "no", "yes", "yes", "no", "yes"]
print(votes.count("yes")) # 4
print(votes.count("no")) # 2
When you need more control over the search — for example, finding the first element that satisfies a condition — use a loop:
scores = [74, 88, 55, 91, 66, 83]
for i in range(len(scores)):
if scores[i] >= 90:
print("First score at or above 90:", scores[i], "at index", i)
break
First score at or above 90: 91 at index 3
The break statement exits the loop as soon as the first matching element is found.
A slice extracts a portion of a list and returns it as a new list. Slicing uses the same notation you learned for strings in Chapter 16.
scores = [88, 74, 91, 65, 83, 79, 95]
# 0 1 2 3 4 5 6
The slice syntax is list[start:stop], where start is the index to begin at (included) and stop is the index to stop before (not included):
print(scores[1:4]) # [74, 91, 65] print(scores[0:3]) # [88, 74, 91] print(scores[4:7]) # [83, 79, 95]
If you omit start, Python starts from the beginning:
print(scores[:3]) # [88, 74, 91]
If you omit stop, Python goes to the end:
print(scores[4:]) # [83, 79, 95]
A full slice scores[:] copies the entire list:
copy = scores[:]
This is useful when you need to work with a copy of a list without changing the original.
You can also use a third argument as the step:
print(scores[::2]) # [88, 91, 83, 95] — every other element print(scores[::-1]) # [95, 79, 83, 65, 91, 74, 88] — reversed
Slicing always produces a new list. The original list is not changed.
scores = [88, 74, 91, 65, 83] first_three = scores[:3] print(first_three) # [88, 74, 91] print(scores) # [88, 74, 91, 65, 83] — unchanged
numbers = [10, 20, 30, 40, 50, 60] print(numbers[2:5]) print(numbers[:4]) print(numbers[3:]) print(numbers[::3])
[30, 40, 50] [10, 20, 30, 40] [40, 50, 60] [10, 40]
Python provides many built-in methods for working with lists. You have already seen append(), remove(), pop(), insert(), index(), and count(). Here are several more that appear frequently in real programs.
sort()sort() rearranges the list in ascending order (smallest to largest for numbers, alphabetical for strings):
scores = [88, 74, 91, 65, 83] scores.sort() print(scores) # [65, 74, 83, 88, 91] names = ["Carlos", "Alice", "Diana", "Bob"] names.sort() print(names) # ['Alice', 'Bob', 'Carlos', 'Diana']
To sort in descending order, pass reverse=True:
scores.sort(reverse=True)
print(scores) # [91, 88, 83, 74, 65]
sort() modifies the list in place. It does not return a new list.
sorted()sorted() is a built-in function (not a method) that returns a new sorted list without changing the original:
scores = [88, 74, 91, 65, 83] ranked = sorted(scores) print(ranked) # [65, 74, 83, 88, 91] print(scores) # [88, 74, 91, 65, 83] — unchanged
Use sort() when you want to rearrange the list itself. Use sorted() when you need a sorted copy and want to keep the original order.
reverse()reverse() reverses the list in place:
items = [1, 2, 3, 4, 5]
items.reverse()
print(items) # [5, 4, 3, 2, 1]
extend()extend() adds all elements from another list to the end of the current list:
group_a = [88, 74, 91]
group_b = [65, 83, 79]
group_a.extend(group_b)
print(group_a) # [88, 74, 91, 65, 83, 79]
This is different from append(), which would add the entire list as a single element:
group_a = [88, 74, 91]
group_b = [65, 83, 79]
group_a.append(group_b)
print(group_a) # [88, 74, 91, [65, 83, 79]] — a list inside a list
clear()clear() removes all elements from a list:
items = [1, 2, 3]
items.clear()
print(items) # []
copy()copy() returns a shallow copy of the list:
original = [10, 20, 30] duplicate = original.copy() duplicate.append(40) print(original) # [10, 20, 30] — unchanged print(duplicate) # [10, 20, 30, 40]
| Method | What It Does | Returns |
|---|---|---|
append(x) | Adds x to the end | None (modifies in place) |
insert(i, x) | Inserts x at index i | None |
remove(x) | Removes first occurrence of x | None |
pop(i) | Removes and returns element at index i | The removed element |
index(x) | Returns first index of x | Integer |
count(x) | Counts occurrences of x | Integer |
sort() | Sorts the list in place | None |
reverse() | Reverses the list in place | None |
extend(other) | Adds all elements of other | None |
clear() | Removes all elements | None |
copy() | Returns a shallow copy | New list |
Methods that modify the list in place generally return None. Methods that read from the list return a value. This is by design and helps you avoid the common mistake of storing the result of a modifying method.
Several patterns appear again and again whenever you work with lists. These patterns are so common that Python has built-in functions for some of them. Knowing both the manual loop approach and the built-in function approach makes you a more flexible programmer.
To add up all elements in a list, use an accumulator:
scores = [88, 74, 91, 65, 83]
total = 0
for score in scores:
total = total + score
print(total) # 401
Python's built-in sum() function does the same thing in one step:
total = sum(scores)
print(total) # 401
Divide the total by the count of elements:
scores = [88, 74, 91, 65, 83]
average = sum(scores) / len(scores)
print(average) # 80.2
Always guard against dividing by zero if the list might be empty:
if len(scores) > 0:
average = sum(scores) / len(scores)
else:
average = 0
To count elements that satisfy a condition, use a counter variable:
scores = [88, 74, 91, 65, 83]
passing = 0
for score in scores:
if score >= 70:
passing = passing + 1
print(passing) # 4
To find the largest element, track the largest value seen so far:
scores = [88, 74, 91, 65, 83]
highest = scores[0]
for score in scores:
if score > highest:
highest = score
print(highest) # 91
Python's built-in max() and min() functions are simpler when you just need the value:
print(max(scores)) # 91 print(min(scores)) # 65
But the loop approach is essential when you need to track more than just the value — for example, when you need to know the index of the maximum:
scores = [88, 74, 91, 65, 83]
highest = scores[0]
highest_index = 0
for i in range(len(scores)):
if scores[i] > highest:
highest = scores[i]
highest_index = i
print("Highest score:", highest)
print("At position:", highest_index)
Highest score: 91 At position: 2
A common pattern is to loop through a list and collect items that pass a test into a new list:
scores = [88, 74, 91, 65, 83]
passing_scores = []
for score in scores:
if score >= 70:
passing_scores.append(score)
print(passing_scores) # [88, 74, 91, 83]
This pattern — start with an empty list, loop through the original, append items that qualify — appears constantly in real programs.
def summarize(scores):
"""Print a statistical summary of a list of scores."""
if len(scores) == 0:
print("No scores to summarize.")
return
total = sum(scores)
count = len(scores)
average = total / count
highest = max(scores)
lowest = min(scores)
passing = 0
for score in scores:
if score >= 70:
passing = passing + 1
print("Count:", count)
print("Total:", total)
print("Average:", round(average, 1))
print("Highest:", highest)
print("Lowest:", lowest)
print("Passing (70+):", passing)
test_scores = [88, 74, 91, 65, 83, 72, 59, 95]
summarize(test_scores)
Count: 8 Total: 627 Average: 78.4 Highest: 95 Lowest: 59 Passing (70+): 6
This function uses multiple list patterns together and wraps them in a reusable design.
[] for later use.len() returns the element count, and the last valid index is always len(list) - 1. Out-of-range access raises IndexError.list[i] replaces an element in place. This differs from strings, which are immutable.append() adds to the end, insert() adds at a position, remove() deletes the first matching value, and pop() removes and returns an element by index. Methods that modify the list return None, so reassigning the list to the method's result is a common bug.for loop visits every element in order. Modifying elements requires index-based iteration with range(len(...)), since the loop variable in for item in list is only a copy of each value.in and not in test membership. index() finds the first position of a value, and count() counts occurrences. A loop with break supports custom search conditions.list[start:stop] (and an optional step) extracts a new list without changing the original. Omitted bounds default to the beginning or end of the list.sort() and reverse() rearrange a list in place; sorted() returns a new sorted list. extend() merges another list's elements; clear() empties a list; copy() duplicates one.Read each code block carefully. Do not run the code yet. Answer the questions below each block.
numbers = [5, 3, 8, 1, 9, 2] print(numbers[0]) print(numbers[-1]) print(numbers[2]) print(len(numbers))
numbers[0] evaluate to?numbers[-1] evaluate to?numbers[2] evaluate to?len(numbers) return?items = ["pen", "notebook", "eraser", "ruler"]
items[1] = "marker"
items.append("scissors")
print(items)
items[1] before the assignment?items[1] after the assignment?items.append("scissors") do to the list?print display?values = [10, 20, 30, 40, 50] result = values[1:4] print(result) print(values)
values[1:4] include?result?values changed by the slice? Why or why not?grades = [78, 92, 65, 88, 71, 55, 90]
high = []
for grade in grades:
if grade >= 80:
high.append(grade)
print(high)
high?grades will be added to high?print display?Trace the following programs step by step. Write down the value of each variable after each line executes.
data = [4, 7, 2, 9, 5]
total = 0
for value in data:
total = total + value
print(total)
Trace the value of total after each iteration of the loop.
| Iteration | value | total after update |
|---|---|---|
| 1 | 4 | 4 |
| 2 | 7 | 11 |
| 3 | 2 | 13 |
| 4 | 9 | 22 |
| 5 | 5 | 27 |
What does the final print display?
words = ["cat", "dog", "fish", "bird"]
for i in range(len(words)):
words[i] = words[i].upper()
print(words)
Trace the value of words[i] after each iteration. What does the final print display?
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
largest = numbers[0]
for n in numbers:
if n > largest:
largest = n
print(largest)
Trace the value of largest after each iteration. What does the final print display?
For each code block, predict exactly what will be printed before running the code.
items = [10, 20, 30, 40, 50] print(items[0] + items[-1]) print(items[2]) print(len(items))
numbers = [5, 3, 8, 1] numbers.append(7) numbers.sort() print(numbers)
letters = ["a", "b", "c", "d", "e"] print(letters[1:3]) print(letters[:2]) print(letters[3:])
values = [10, 20, 30, 40, 50]
count = 0
for v in values:
if v > 25:
count = count + 1
print(count)
fruits = ["apple", "banana", "cherry"]
fruits.remove("banana")
fruits.insert(0, "mango")
print(fruits)
print(len(fruits))
Each program below contains one or more errors. Identify the error, explain what is wrong, and write the corrected code.
scores = [88, 74, 91, 65, 83] print(scores[5])
What type of error occurs? What is the fix?
numbers = [1, 2, 3, 4, 5] numbers = numbers.append(6) print(numbers)
What is wrong? What will print(numbers) display, and why? Write the corrected version.
prices = [9.99, 4.49, 14.99, 2.99]
total = 0
for price in prices:
price = price + total
print(total)
The programmer wants to compute the total of all prices, but the program prints 0. Explain why this is wrong and write the corrected version.
items = ["pen", "notebook", "eraser"]
for i in range(len(items)):
print(items[i + 1])
What error will this program produce? At what point during execution does it occur? Write a corrected version that prints every item.
names = ["Alice", "Bob", "Carlos"]
if "Diana" in names:
pos = names.index("Diana")
print("Diana is at position", pos)
This program does not crash, but it does not print anything when Diana is not in the list. Is this a bug or correct behavior? Add an else clause to handle both cases.
These exercises ask you to write, complete, or modify a program. Keep your solutions clean and readable.
scores = [72, 85, 91, 60, 78, 88, 95, 65, 70, 82]. Compute the average. Then print all scores that are above the average, one per line. Label the output clearly.reverse(). Write a function called reverse_list(items) that takes a list and returns a new list with the elements in reverse order. Do not use the reverse() method or [::-1] slicing. Use a loop. Test your function with several lists, including an empty list.remove_duplicates(items) that takes a list and returns a new list with all duplicate values removed, keeping the first occurrence of each value and preserving the order of first appearances. Example: remove_duplicates([3, 1, 4, 1, 5, 9, 2, 6, 5]) should return [3, 1, 4, 5, 9, 2, 6].second_largest(numbers) that returns the second largest value in a list of numbers. Assume the list has at least two elements and that not all elements are identical. Do not use sort(). Use a loop to find the two largest values.This project brings together the main skills from this chapter — creating lists, appending, removing, searching, and iterating — into a small interactive program.
Write a console-based to-do list manager. The program should let the user:
=== To-Do List Manager === 1. View tasks 2. Add task 3. Mark task done 4. Search for task 5. Quit Choose an option: 1 Your tasks: (No tasks yet) Choose an option: 2 Enter new task: Buy groceries Task added. Choose an option: 2 Enter new task: Finish homework Task added. Choose an option: 2 Enter new task: Call dentist Task added. Choose an option: 1 Your tasks: 1. Buy groceries 2. Finish homework 3. Call dentist Choose an option: 3 Enter task to mark done: Finish homework "Finish homework" removed. Choose an option: 1 Your tasks: 1. Buy groceries 2. Call dentist Choose an option: 4 Enter task to search for: Buy groceries "Buy groceries" is on your list. Choose an option: 5 Goodbye!
while loop to keep the menu running until the user quits.show_tasks(tasks) — prints all tasks; add_task(tasks, task) — appends the task; complete_task(tasks, task) — removes the task if found; search_task(tasks, task) — checks and reports whether the task is on the list.Let the user reorder a task (move it to a different position in the list). Track how many tasks have been completed during the session and display the count when the user quits. Let the user view tasks sorted alphabetically without changing the original order.
These problems require skills from earlier chapters alongside the list skills from this chapter. This is by design — good programmers keep earlier knowledge active.
Write a function called clamp_all(numbers, low, high) that takes a list of numbers and two limits. It should return a new list where every value below low is replaced with low and every value above high is replaced with high. Values already between low and high stay unchanged.
Example: clamp_all([5, -3, 12, 7, 0, 15], 0, 10) returns [5, 0, 10, 7, 0, 10].
Write a program that generates a list of the first twenty multiples of a number entered by the user (1× through 20×) and then prints only the even multiples.
Write a function called initials(names) that takes a list of full names (each as a single string, like "Alice Johnson") and returns a list of initials (like "A.J.").
The following function is supposed to return a list of all numbers from 1 to n that are divisible by 3. Find the bug and fix it.
def divisible_by_three(n):
result = []
for i in range(1, n + 1):
if i % 3 == 0:
result.append(i)
print(result)
numbers = divisible_by_three(20)
print(numbers)
You have not yet learned about files, but think about this problem: if a program reads a text file where each line contains one student's name, what data structure would you use to store all the names? How would you count them? How would you sort them? Write a brief explanation, and then write the code you would use if the names were already in a list.
Chapter 17 gave you lists: ordered, mutable collections you can iterate, search, slice, and grow. Lists are the right structure when order matters and when you expect to add, remove, and update elements freely.
But Python has other built-in collection types designed for different situations. Chapter 18 introduces three more. Tuples are ordered collections like lists, but they cannot be changed after they are created — you will learn when fixed data is actually an advantage. Sets are unordered collections of unique values, useful when you need to eliminate duplicates or test membership very quickly. Dictionaries store key-value pairs, letting you look up information by a meaningful name instead of a position number, and are among the most powerful and widely used data structures in Python.
Understanding when to reach for a list, a tuple, a set, or a dictionary is one of the most practical skills a programmer can develop. Chapter 18 begins that process.