Skip to content
wannacode

A list that grows

Problem

The cart is full

You are writing the shopping cart of an online store, and you keep the items in a list, as in the lesson on arrays: each item at its own index. But you cannot write past the end: cart[3] = "Butter" stops the program with IndexError: list assignment index out of range. So for a fourth item you make a new, longer list. And when the customer removes an item, a hole is left:

cart = ["Bread", "Milk", "Coffee"]
# A fourth item: cart[3] = "Butter" stops with IndexError,
# so we make a new, longer list.
cart = cart + ["Butter"]
# The customer removes the milk.
cart[1] = None
for item in cart:
print(item)
Output
Bread
None
Coffee
Butter
Now every loop over the cart has to skip None. And the number of items is no longer len(cart).

That is a lot of code for something every store does all the time.

The idea

append and remove: the list looks after itself

The list has methods that do this for you. Inside, it still keeps the elements in an array, but it does the copying and shifting by itself. A collection that grows and shrinks like this is called a list:

cart = []
cart.append("Bread")
cart.append("Milk")
cart.append("Coffee")
cart.append("Butter")
cart.remove("Milk")
print(cart)
print("Items:", len(cart))
Output
['Bread', 'Coffee', 'Butter']
Items: 3
  • append adds at the end.
  • remove removes the element with that value and closes the hole: "Coffee" moves into the place of "Milk". If the value is there several times, it removes the first one.
  • len(cart) is how many elements there are right now.
  • print prints the whole list in square brackets, with the text in quotes.

Here is what the list does inside. You do not choose how many places the array has. CPython does, the program that runs your code. A list of 4 items written in the code has exactly 4 places, so the array fills up on the fifth:

cart = ["Bread", "Milk", "Coffee", "Butter"]
cart.append("Cheese")
cart.remove("Milk")
print(cart)
print("Items:", len(cart))
Step by step, in words
  1. cart points to a list object. Inside there is an ob_size counter and an ob_item array with 4 places.
  2. The items from the code fill the places one by one. The array is full: 4 of 4.
  3. There is no room. append allocates an array with 8 places, and the elements go into it.
  4. ob_item now points to the new array. "Cheese" goes into the first free place.
  5. remove removes "Milk", and everything after it moves one place to the left. No hole is left.
  6. From the outside you see only a list of four items. CPython decides how many places it has inside: the language does not promise it.

The idea

What goes in a list

A list does not ask what you put in it. Text and a number can sit side by side:

cart = []
cart.append("Bread")
cart.append(5)
print(cart)
Output
['Bread', 5]

That looks handy, but the error shows up later, where you expect text. For example, a loop that prints the items in capital letters:

cart = ["Bread", 5]
for item in cart:
print(item.upper())
Error
AttributeError: 'int' object has no attribute 'upper'

"Bread" gets printed, and the program stops at the number. So one list holds one kind of thing: only items, only prices.

You can write that down with a type hint: list[str] means "a list of text".

cart: list[str] = []
cart.append("Bread")
cart.append(5)
print(cart)
Output
['Bread', 5]

Python does not check the hint while the program runs: the number still gets in. Your editor and tools like mypy check it before you run the program. This lesson has no hints, but you will see them in other people's code.

The idea

Indexes: [i], insert, index, in

A list has indexes like an array, from 0 to len(tasks) - 1, and you write them with the same [ ]:

tasks = ["March invoice", "Call the bank", "Warehouse report"]
print(tasks[0])
tasks[1] = "Meeting at the bank"
tasks.insert(0, "Coffee with the team")
print(tasks)
print(tasks.index("Warehouse report"))
print("Call the bank" in tasks)
Output
March invoice
['Coffee with the team', 'March invoice', 'Meeting at the bank', 'Warehouse report']
3
False
  • tasks[i] reads, and tasks[i] = x replaces.
  • insert(i, x) inserts at index i and shifts the rest to the right.
  • index(x) searches and returns the index, and x in tasks only says whether it is there.
More precisely, if you are curious
  • An index outside the list stops the program. A negative index counts from the back: tasks[-1] is the last task. And index of something that is not there also stops the program, so ask with in first:
tasks = ["March invoice", "Call the bank", "Warehouse report"]
try:
print(tasks[3])
except IndexError as e:
print(f"IndexError: {e}")
print(tasks[-1])
try:
tasks.index("Payroll")
except ValueError as e:
print(f"ValueError: {e}")
Output
IndexError: list index out of range
Warehouse report
ValueError: 'Payroll' is not in list
  • in and index compare with ==. That is why they find text that is equal, not only the same object.
  • Inserting and removing at the front are slow in a big list: every element after the index moves by one place. For a million elements, that is a million moves.

Try it

Who is next?

The list of patients waiting at the doctor's. Ivan goes in, and Ana has an appointment, so she takes second place.

What will the program print?

waiting = ["Ivan", "Maria", "Peter"]
waiting.pop(0)
waiting.insert(1, "Ana")
print(waiting, len(waiting))
Answer

The idea

Looping over the elements

You loop over a list with for when you need only the elements. When you also need the number, there is enumerate:

cart = ["Bread", "Coffee", "Butter"]
for item in cart:
print(f"- {item}")
for number, item in enumerate(cart, start=1):
print(f"{number}. {item}")
Output
- Bread
- Coffee
- Butter
1. Bread
2. Coffee
3. Butter

enumerate(cart, start=1) gives two values at each step: the number and the element. Without start=1, the numbers start at 0, like the indexes. You can also use an index, as in the lesson on arrays, with for i in range(len(cart)), but enumerate is the usual way in Python.

More precisely, if you are curious
  • Do not remove elements from a list while you loop over it with for. The program does not stop, but it skips elements:
cart = ["Bread", "Bread", "Milk"]
for item in cart:
if item == "Bread":
cart.remove(item)
print(cart)
Output
['Bread', 'Milk']

The second "Bread" stayed. remove shifted everything to the left, and the loop went on from the next index and never saw it. There is no error, so a miss like this is hard to catch.

  • If you have to remove, loop with an index from the back to the front. That way, removing does not shift the elements you have not checked yet:
cart = ["Bread", "Bread", "Milk"]
for i in range(len(cart) - 1, -1, -1):
if cart[i] == "Bread":
cart.pop(i)
print(cart)
Output
['Milk']
In the "Modern Python" module you will see a shorter way that makes a new list with only the elements you want.

The idea

Numbers in a list

A list of scores is like a list of text. The number sits in the list as it is:

scores = [85, 64, 91]
total = 0
for score in scores:
total += score
print("Average:", total / len(scores))
print("Average:", sum(scores) / len(scores))
Output
Average: 80.0
Average: 80.0
  • / gives a decimal number, so the average is 80.0, not 80.
  • sum adds up all the numbers in the list. The two lines compute the same thing.
More precisely, if you are curious
  • In Python, a number is an object too. For small numbers, from -5 to 256, Python keeps ready-made objects, and for bigger ones it makes new ones. That is why is, which asks "is it the same object?", gives different answers:
a = int("256")
b = int("256")
c = int("257")
d = int("257")
print(a is b)
print(c is d)
print(c == d)
Output
True
False
True

Compare numbers and text with ==: it asks whether the values are equal. is is for None: if found is None.

Try it

Find the bug

A theater keeps the taken seats in a list. A customer gives up seat 2, and the program should print [12, 7, 5]. Instead it prints [12, 2, 5]. Which line has the bug?

Pick the line with the bug.

The idea

Sorting and a list that does not change

sort() sorts the list in place: text in alphabetical order, numbers by size. sorted(...) does not touch anything. It returns a new, sorted list.

cities = ["Sofia", "Plovdiv", "Varna"]
cities.sort()
print(cities)
prices = (30, 5, 12)
print(sorted(prices))
Output
['Plovdiv', 'Sofia', 'Varna']
[5, 12, 30]

The prices are in round brackets: (30, 5, 12). This is a tuple, a list that is immutable. You can read it and loop over it, but you cannot add, remove or sort in it:

days = ("Mon", "Tue", "Wed")
days.append("Thu")
Error
AttributeError: 'tuple' object has no attribute 'append'

That is why the prices above are sorted with sorted: it returns a new list, and the tuple stays the same. A tuple is for things that do not change: the days of the week, the two coordinates of a point.

More precisely, if you are curious
  • Text is sorted by the codes of its characters. Capital letters come before lowercase ones, so "Pear" comes before "apple":
fruit = ["apple", "Pear", "banana", "Grape"]
fruit.sort()
print(fruit)
Output
['Grape', 'Pear', 'apple', 'banana']
  • How to sort your own objects, for example products by price, you will see in the lesson "Classes inside classes".

Exercise

Lists in classes

Solve the tasks in your editor. The list is a field of the class with a _ in front ("do not touch from outside"), and the code below the classes only calls its methods.

1. A bookshelf. A BookShelf class with a add_book(title) method that puts the book at the end, and take_book(), which returns and removes the first book (or None if the shelf is empty). Add count() and __str__(). Add "Under the Yoke", "Tobacco" and "The Iron Candlestick", take one book and print the rest.

Output
Took: Under the Yoke
2 left: ['Tobacco', 'The Iron Candlestick']

2. An event log. An Event class with a date and a description, and an EventLog class with the methods add_event(date, description) and get_events_between(start, end), which returns a list of the events between the two dates, inclusive. The dates are text in the form 2026-03-31. In this form, the text sorts the same way as the dates, so <= and >= compare them correctly. Add four events: 2026-01-15 "Annual stocktake", 2026-03-31 "First quarter report", 2026-05-20 "New coffee supplier", 2026-09-01 "New school year". Print the ones from 2026-03-01 to 2026-06-30.

Output
2026-03-31 First quarter report
2026-05-20 New coffee supplier

3. A notebook. A Note class with a title and content, and a Notebook class with the methods add_note, find_note(title) (returns the note or None), delete_note(title) (returns True if it deleted something) and count(). Add the notes "OOP", "Collections" and "SQL", find "Collections", delete "OOP" and check that it is gone.

Output
Collections: the list grows by itself
Deleted: OOP
Notes: 2
There is no OOP note.

4. Mental math. A Question class with a text and the right answer, and a MathGame class with a list of questions, an add_question method and a start() method. start asks the questions one by one, reads the answer and at the end says how many were right. The questions are 2 + 2, 3 * 4 and 15 - 9.

Input
4
12
7
Output
2 + 2 = Correct!
3 * 4 = Correct!
15 - 9 = Wrong, the answer is 6.
Score: 2 out of 3

More tasks

  • No repeats. Read 6 names and collect them in a list, skipping the ones that are already in it. For the input Ivan, Maria, Ivan, Ana, Maria, Peter the output is ['Ivan', 'Maria', 'Ana', 'Peter'].
  • The most expensive item. A list holds prices. Find the highest one and its index, without max.
  • Reverse the list. Write a function that returns a new list with the same elements in reverse order. Do not change the original.
  • A shopping list. Read the commands add milk, remove milk, list and end, and keep the list between them. The command to remove an item that is not there says "No such item."

Test

Check yourself

1. Why are append and remove handier than writing by index?
2. So far cart holds only text. What does cart.append(5) do?
3. seats is [12, 2, 7, 5]. What is left after seats.pop(2)?
4. tasks has 3 tasks. What does print(tasks[3]) do?
5. days = ("Mon", "Tue"). What happens at days.append("Wed")?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·