8  Iteration and Iterable Datatypes

8.1 Lists

Until now we have worked with values and variables that hold a single value, string, image, etc. Now we will look into container data types that can store a sequence of other datatypes.

Lists are common containers that can hold a sequence of elements. We can create a list by putting values inside square brackets, separating the values with commas:

numbers = [2, 1, 4, 3]
print(numbers)
type(numbers)
[2, 1, 4, 3]
list

Each element can be accessed by an index. The index of the first element in a sequence in Python is 0 (in some other programming languages, like MATLAB, that would be 1). Negative indices can be used to access elements from the end of the sequence: -1 points to the last element, and -2 to the second to last.

print("The first element is the number", numbers[0])
print("The second to last element is the number", numbers[-2])
The first element is the number 2
The second to last element is the number 4

Lists and other sequences can also be sliced, which means accessing a contiguous range of items. The range 1:4 gets the items at indices 1, 2 and 3. By leaving out the first index, e.g. :2, the range starts at the beginning. By leaving out the last index, e.g. 1:, the range will end at the end. Leaving out both the first and last index, :, will include all items.

Note that indexing and slicing NumPy arrays works much like indexing and slicing lists and other Python sequences. In fact, NumPy arrays were designed with an interface that allows them to be used like ordinary Python sequences in many ways.

print(numbers[1:4])  # Get items at indices 1, 2, 3
print(numbers[:2])  # Get the first two items
print(numbers[-2:])  # Get the last two items
print(numbers[:])  # Get all items
[1, 4, 3]
[2, 1]
[4, 3]
[2, 1, 4, 3]

Python has various functions that operate on lists and other sequences:

print("The length of list is:", len(numbers))
print("The sum of the items in the list is:", sum(numbers))
print("The maximum number is:", max(numbers))
print("The sorted list is:", sorted(numbers))
The length of list is: 4
The sum of the items in the list is: 10
The maximum number is: 4
The sorted list is: [1, 2, 3, 4]

Python lists are not limited to storing numbers, but they can hold any datatype:

import datetime

words = ["pixel", "vector", "data"]
mess = [3.14, "what a mess", datetime.datetime.now(), type(words)]
print(mess)
[3.14, 'what a mess', datetime.datetime(2026, 9, 25, 14, 47, 27, 78921), <class 'list'>]

External course material

Lists can be changed and resized in many convenient ways. Review these pages about comparisons and do the exercises:

8.2 Tuples

A tuple is similar to a list in that it’s a sequence of elements. However, tuples can not be changed once created; they are immutable. Tuples are created by placing comma-separated values inside parentheses () (instead of square brackets []).

a_tuple = (1, 2, 3)  # Tuples use parentheses
another_tuple = ('red', 'green', 'blue')

a_list = [1, 2, 3]  # Lists use square brackets
a_list[1] = 5  # List items can be modified
print(a_list)
[1, 5, 3]
a_tuple[1] = 5  # Tuples are immutable
print(a_tuple)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[8], line 1
----> 1 a_tuple[1] = 5  # Tuples are immutable
      2 print(a_tuple)

TypeError: 'tuple' object does not support item assignment

The items in sequences such as list and tuples can be unpacked to individual variables. This can be convenient when you are working with e.g. vectors:

spacing_in_mm = (0.9, 0.9, 4.8)
dx, dy, dz = spacing_in_mm
print(dz)
4.8

8.3 Dictionaries

A dictionary is another way to store a sequence of items. In dictionaries, however, this is done with keys and values. The keys are used to look up values. This can be useful for several reasons, one example is to store model settings, parameters or variable values for multiple scenarios.

subject = {'patient id': '007', 'number of scans': 3, 'modality': 'CT'}
subject
{'patient id': '007', 'number of scans': 3, 'modality': 'CT'}

We can access dictionary items by their key:

subject['patient id']
'007'

And we can add new key-value pairs like that:

subject['age'] = 42
subject
{'patient id': '007', 'number of scans': 3, 'modality': 'CT', 'age': 42}

Dictionary items are key-value pairs. The keys are changeable and always have to be unique. The values within a dictionary are mutable, and don’t have to be unique.

print("Dictionary keys: ", subject.keys())
print("Dictionary values: ", subject.values())
print("Dictionary items (key, value): ", subject.items())
Dictionary keys:  dict_keys(['patient id', 'number of scans', 'modality', 'age'])
Dictionary values:  dict_values(['007', 3, 'CT', 42])
Dictionary items (key, value):  dict_items([('patient id', '007'), ('number of scans', 3), ('modality', 'CT'), ('age', 42)])

8.4 Iteration

Once we have a sequence of items, we can make our Python code do something for each item in the sequence. For each patient in your study, for each item in your measurements, for each pixel in your image… This is called iteration, and it can be done using a for-loop:

measurements = [2.3, 4.2, 1.2]

for item in measurements:  # Iterate over all items in measurements
    x = item ** 2  # Do something with the item
    print(f'{item}² is {x:.2f}')  # Specify to print x as a floating point value with two decimals.
2.3² is 5.29
4.2² is 17.64
1.2² is 1.44

Note that (similar to if-statements) Python needs indentation (4 spaces) to determine which lines of code are part of the for-loop.

If we want to also get the index, we can use the built-in function enumerate. The function enumerate returns a tuple with the index and the item, which can unpack in the for-loop:

for index, item in enumerate(measurements):
    print(f'Measurement {index}: {item}')
Measurement 0: 2.3
Measurement 1: 4.2
Measurement 2: 1.2

A for-loop can be applied to any sequence, including dictionaries. Using the dictionary’s functions keys(), values() or items(), we can choose to either iterate over the keys, the values, or both. Let’s take our dictionary from the previous section and inspect the dictionary items:

for key, value in subject.items():  # my_dict.items() returns tuples with a key and a value
    print(f'The value of "{key}" is {value}, and the type is {type(value)}')
The value of "patient id" is 007, and the type is <class 'str'>
The value of "number of scans" is 3, and the type is <class 'int'>
The value of "modality" is CT, and the type is <class 'str'>
The value of "age" is 42, and the type is <class 'int'>

for-loops and if-statements can be nested. This means that you can use a for-loop, or if-statement inside another one. The indentation will then be a multiple of 4 spaces:

measurements = {
    "002": [1.2, 5.4, 6.7],
    "005": [1.7, 5.1, 6.6, 7.3],
    "007": [1.3, 4.7],
}

# Iterate over all measurements and list the values that are >5 mL.
for key, measurement in measurements.items():
    for i, value in enumerate(measurement):
        if value > 5:
            print(f'Patient {key}, measurement {i} > 5 mL: {value} mL')
Patient 002, measurement 1 > 5 mL: 5.4 mL
Patient 002, measurement 2 > 5 mL: 6.7 mL
Patient 005, measurement 1 > 5 mL: 5.1 mL
Patient 005, measurement 2 > 5 mL: 6.6 mL
Patient 005, measurement 3 > 5 mL: 7.3 mL

Iteration in for-loops can be controlled with the keywords break and continue. The keyword break stops iterating, and continue stops the execution of the code block for the current iteration, and continues with the next:

measurement = [1.7, 5.1, 6.6, 7.3]

# Find the first value larger than 5:
for i, value in enumerate(measurement):
    if value > 5:
        print("A value > 5 was found. Stop iterating.")
        break  # Stop iterating over the measurements.
    else:
        print("Value <= 5.")

print(f'Measurement {i}: {value} mL')
Value <= 5.
A value > 5 was found. Stop iterating.
Measurement 1: 5.1 mL
# Act only on values >= 7:
for i, value in enumerate(measurement):
    if value < 7:
        print(f"{value} < 7. Continue iterating.")
        continue  # Continue with the next iteration.

    print(f"This is only executed for values >= 7. In this case: {value}")
1.7 < 7. Continue iterating.
5.1 < 7. Continue iterating.
6.6 < 7. Continue iterating.
This is only executed for values >= 7. In this case: 7.3

The range function is used to iterate over a range of integer numbers. for i in range(10), for example, iterates over 0, 1, ..., 9. A start, stop, and step value can be specified:

values = [7, 3, 8, 5, 3]

# Revert the values in the list
reverse = []  # An empty list to which we'll add the values
n = len(values)  # Number of items in the list
for i in range(n, 0, -1):  # Iterate backwards from n to 1
    reverse.append(values[i - 1])  # Index starts at 0, hence i - 1
print(reverse)
[3, 5, 8, 3, 7]

External course material

Review this page about the range function and do the exercises:

8.5 The any, all, and in Functions

In the previous chapter, we saw that the or and and operators can be used to determine whether either or both of two statements evaluate to True. The any() and all() functions provide similar functionality for sequences: any([a, b, c]) returns True if at least one of the values evaluates to True, and all([a, b, c]) returns True only if they all of them do. The in operator can be used to check whether a particular value occurs in a sequence.

zeros = [0, 0, 0, 0]
nonzeros = [1, 2, 3, 4]
mixed = [1, 0, 0, 3]

if any(mixed):
    print('"mixed" contains a nonzero value')

if not any(zeros):
    print('"zeros" contains no nonzero values')

if all(nonzeros):
    print('All values in "nonzeros" are nonzero')

if 2 in nonzeros:
    print('"nonzeros" contains the number 2')
"mixed" contains a nonzero value
"zeros" contains no nonzero values
All values in "nonzeros" are nonzero
"nonzeros" contains the number 2

8.6 List Comprehension

List comprehension allows you to construct new sequences given a sequence and simple logic. It uses the syntax newlist = [expression for item in iterable], or optionally: newlist = [expression for item in iterable if condition]. The same result can be achieved with a for-loop and if-statements, but list comprehensions provides a more compact syntax.

As an example, consider the following for-loop that compiles a list of patient ID’s for which the imaging modality is 'CT':

subjects = [
    {'patient id': '002', 'number of scans': 5, 'modality': 'CT'},
    {'patient id': '005', 'number of scans': 3, 'modality': 'MR'},
    {'patient id': '007', 'number of scans': 3, 'modality': 'CT'},
]

# Compile a list of all subjects that were scanned on a CT scanner.
ct_patients = []
for subj in subjects:  # Iterate over all subjects.
    if subj['modality'] == 'CT':  # Check if the modality is CT.
        ct_patients.append(subj['patient id'])  # Add the patient to the list.
print(ct_patients)
['002', '007']

The same list can be constructed using list comprehension. Read this code like “get the patient ID for every subject in the list of subjects, but only if the modality equals CT”:

# Compile a list of all subjects that were scanned on a CT scanner.
ct_patients = [subj['patient id'] for subj in subjects if subj['modality'] == 'CT']
print(ct_patients)
['002', '007']

In a similar way, we can count the total number of CT scans:

# Count the total number of CT scans.
total_ct_scans = sum(subj['number of scans'] for subj in subjects if subj['modality'] == 'CT')
print(total_ct_scans)
8

External course material

Review this page about list comprehension and do the exercise: