Mid-October 2026: in Year 2 of Italian technical institutes (and of the applied sciences high school) you have finished variables, if and loops, and now the computer science teacher moves on to data structures: lists, tuples, dictionaries. The test arrives in five to eight weeks, between late November and mid-December, and it is almost always a written test in which you have to read some code and predict its output, or write a small function by hand. I am writing this post now because lists are only learned well by writing them, and two months is enough to do it calmly.
If you lack the basics on loops, start again from Python programming from scratch and from flowcharts and pseudocode. Here I take for granted that for, while and if do not frighten you.
Why data structures are needed
With variables alone, to keep six grades you need six names: g1, g2, g3… and the day the grades number sixty the program is unmanageable. A list gathers the values under a single name, in order, and lets you repeat the same operation on all of them with a loop. The rest of the chapter is deciding which container to use, and which traps to avoid.
Lists: indexes, slicing, methods
A list is written between square brackets. Indexes start from zero, and negative ones count from the end.
grades = [6, 4, 8, 5, 9, 7]
print(grades[0]) # 6
print(grades[-1]) # 7
print(grades[1:4]) # [4, 8, 5]
print(grades[::2]) # [6, 8, 9]
print(len(grades)) # 6
Slicing grades[a:b] takes from index a included up to b excluded: it is the rule that costs the most marks, because people forget that the right-hand end is not there. grades[1:4] has three elements, not four. The third number is the step: [::2] skips one element each time.
The methods used most:
append(x)addsxat the end;insert(i, x)insertsxat positioni, shifting the others;remove(x)deletes the first occurrence of the valuex;pop()deletes and returns the last element,pop(i)the one at positioni.
grades.append(10)
grades.insert(0, 3)
print(grades) # [3, 6, 4, 8, 5, 9, 7, 10]
grades.remove(3)
last = grades.pop()
print(last, grades) # 10 [6, 4, 8, 5, 9, 7]
Note the difference between remove (searching by value) and pop (searching by position): mixing them up is a classic in tests.
Looping over a list and the simple comprehension
To loop over a list you use for, with no need for indexes:
total = 0
for g in grades:
total += g
print(total, total / len(grades), max(grades)) # 39 6.5 9
When you build a new list by filtering or transforming another, Python offers the list comprehension, which is just a compact way of writing the loop with append:
failing = [g for g in grades if g < 6]
print(failing) # [4, 5]
It reads: “for each g in grades, if g is less than 6, put it in the new list”. If it does not come naturally at first, write the long loop first and then shorten it: in the oral and in the written test both are fine.
Tuples: lists that cannot be changed
A tuple is written with round brackets and works like a list, but it is immutable: once created you cannot change, add or remove elements.
point = (3, 5)
print(point[0]) # 3
point[0] = 1 # TypeError: 'tuple' object does not support item assignment
They are used for data that must not change by design: coordinates, a date (day, month, year), the return value of a function that gives two results. If you write point[0] = 1 the program stops with an error, and that is precisely the purpose: immutability protects you from accidental changes.
Dictionaries: key and value
A dictionary associates a value with each key, and is written between curly brackets. You look things up not by position but by key.
d = {"a": 1}
print(d.get("z")) # None
print(d.get("z", 0)) # 0
d["z"] # KeyError: 'z'
Accessing with d["z"] a key that does not exist gives KeyError; d.get("z", 0) instead returns a fallback value, which here is 0. To loop over everything you use items(), which returns the key-value pairs:
for key, value in d.items():
print(key, value)
Strings: a few notes
A string behaves like a list of characters, for indexing and slicing, but is immutable like a tuple. Everything that holds for lists about indexes and slices holds here too.
s = "python"
print(s[0], s[-1], s[1:4], s.upper(), len(s)) # p n yth PYTHON 6
For work on strings and files, the rest is in the dedicated post (strings and files in Python).
When to use what
| Structure | Ordered | Mutable | Used for |
|---|---|---|---|
| List | yes | yes | sequences of values that change (grades, scores) |
| Tuple | yes | no | fixed data (coordinates, dates) |
| Dictionary | by key | yes | associating a name with a value (counts, address books) |
Practical rule: if you need “element number 3” use a list; if you need “the value associated with Mario” use a dictionary.
Exercise 1: statistics on a list
Question. Given grades = [6, 4, 8, 5, 9, 7], calculate the mean and build the list of failing grades (below 6).
Working. The sum of the six numbers is , the mean is . The filter leaves only 4 and 5.
grades = [6, 4, 8, 5, 9, 7]
mean = sum(grades) / len(grades)
failing = [g for g in grades if g < 6]
print(mean) # 6.5
print(failing) # [4, 5]
To practise the same logic with more items, the loop and array problems of Year 3 are the natural next step, and for a review of the working method see the computer science page.
Typical mistakes with lists
This is where marks are lost, and it is worth looking at them one by one.
Index out of range. A three-element list has indexes 0, 1, 2. Writing list[3] gives IndexError: list index out of range. When a while or a for with range goes too far, check whether you used <= instead of <.
Modifying the list while you loop over it. It is the most insidious mistake, because it gives no message: the program runs and gives a wrong result. We see it in exercise 2.
Copy versus reference. b = a does not copy the list: it gives the same list a second name.
a = [1, 2, 3]
b = a
b.append(4)
print(a) # [1, 2, 3, 4] a has changed!
c = a.copy()
c.append(5)
print(a, c) # [1, 2, 3, 4] [1, 2, 3, 4, 5]
To get an independent copy you need a.copy() or a[:].
KeyError. Accessing a missing key with square brackets. Remedy: get, or an if key in dictionary beforehand.
Exercise 2: removing the even numbers (and the bug nobody sees)
Question. Given numbers = [2, 4, 6, 7, 8, 10], delete all the even numbers.
First attempt, wrong:
numbers = [2, 4, 6, 7, 8, 10]
for n in numbers:
if n % 2 == 0:
numbers.remove(n)
print(numbers) # [4, 7, 10]
You expect [7], instead 4 and 10 appear. The reason: the for loops over the list keeping an internal index. On the first iteration n is 2 (index 0), which is removed; the list becomes [4, 6, 7, 8, 10] and the index advances to 1, so it skips the 4 and arrives at 6. Every removal makes the elements shift and the loop skips one.
The correct solution: do not modify the list you are looping over, but build a new one.
numbers = [2, 4, 6, 7, 8, 10]
odd = [n for n in numbers if n % 2 != 0]
print(odd) # [7]
Alternatively you can loop over a copy, with for n in numbers.copy():. In both cases, the list the loop reads does not change while it is being read.
Exercise 3: counting words with a dictionary
Question. Given the sentence "the cat and the dog and the mouse", count how many times each word appears and print those that appear more than once.
Working. split() breaks the sentence into a list of words. For each one: if it is already a key of the dictionary I increase the counter, otherwise I insert it with value 1.
sentence = "the cat and the dog and the mouse"
freq = {}
for word in sentence.split():
if word in freq:
freq[word] += 1
else:
freq[word] = 1
print(freq)
# {'the': 3, 'cat': 1, 'and': 2, 'dog': 1, 'mouse': 1}
for word, n in freq.items():
if n > 1:
print(word, n)
# the 3
# and 2
With get the whole if/else block shortens to one line: freq[word] = freq.get(word, 0) + 1. The result is identical; in a test the long version is perfectly fine, if you write it correctly.
This pattern (the dictionary as a counter) is one of the most requested in tests, because it combines loop, condition and data structure. Learn it by heart in its if/else form.
Mistakes that cost marks
- Forgetting that slicing excludes the right-hand end.
- Starting from 1 instead of 0 with indexes.
- Confusing
remove(value) withpop(position), orappendwithinsert. - Forgetting the colon, or the indentation, after
forandif. - Writing
b = athinking you have copied. - Modifying a list inside the loop that iterates over it.
- Using
d[key]without checking that it exists. - In paper tests, writing the output without following the code line by line: tracing by hand is always the safe route.
How to prepare over the coming weeks
- Weeks 1-2: write out by hand, on paper, all the snippets in this post and predict the output before running them. Then run them and compare.
- Weeks 3-4: rewrite the three exercises yourself without looking at the solution. Change the data (other grades, another sentence) and check again.
- Week 5: do mixed exercises, for example “given a list of names, build a dictionary with the length of each”. If a pattern blocks you, go back to the basic example.
- Every time the code gives an error, read the message:
IndexError,KeyError,TypeErroralready tell you where to look.
You do not need hours a day: three forty-minute sessions a week, with the code written by hand, are worth more than rereading the whole notebook.
If you would like to go over a real test, or if a step just will not unblock, write to me through the contact page: I am happy to do a free introductory call on the exercises you have been given.