Appendix C: Python list processing

Python has several established idioms for handling lists (and other collections) that are not strictly necessary to use if you write Python code, but that are very useful to know. Many Python programmers use these idioms, and practised programmers will recognise them instantly.

Below are some of the most common of the idioms for lists. Similar idioms exist for other collections.

The first of these idioms, Iteration, exists in a similar way in many languages, while the others have a specific "Pythonesque" quality – they could be written using more general patterns, but Python has its own idiom.

C.1. Iteration

Iteration accesses every element in a list in turn.

Accessing the elements

The standard for-loop processes every element in a list.

Screenshot of the code below
marks  = [83,56,32,74]
for m  in marks  :
    print(m)

Output:

83
56
32
74

Accessing elements with their index

The enumerate function delivers an index and an element:

Screenshot of the code below
marks  = [83,56,32,74]
for idx,m  in enumerate(marks)  :
    print(f"{idx}: {m}")

Output:

0: 83
1: 56
2: 32
3: 74

C.2. Checking for empty lists

A standard pattern to check for an empty list – in any language – uses a check for its length:

Screenshot of the code below
if len(list) == 0  :
    pass

"Truthyness" in Python

In Python, programmers often make use of a concept of truthyness: certain values in Python are considered "truthy", while others are "falsey".

"Truthy" values, when used in an if-statement, are treated as True, while "falsey" values are treated as False.

Falsey values include 0, None, the zero-length string ("") and the empty list ([ ]). This means that the list itself can be used in the condition.

Empty list the Python way

The Python idiom makes use of the falseyness of the empty list to check for an empty list:

Screenshot of the code below
list = []
if list :
    print("list is not empty")
else:
    print("list is empty")

Output:

list is empty

We are hesitant to make too much use of this feature because its behaviour can be more difficult to understand. In other programming languages truthyness can behave differently: for example in JavaScript the empty list is truthy not falsey.

C.3. Comprehensions

A list comprehension creates a new list as a mapping from an existing list. They are a replacement for a general pattern that would be written without comprehensions as:

Screenshot of the code below
list  = [1,2,3,4]
new_list  = []
for elem  in list  :
    if condition  :
        new_list.append(expression)

The pattern above can be written using a list comprehension as a one-line statement, without a loop. It takes the form

Screenshot of the code below
new_list = [expression for elem in list if condition]

Comprehensions can be used for map and filter operations, and combinations of these two. Examples are shown below.

C.4. Comprehensions for mapping

A map operation creates a new list from a given list with the same length. Every element in the new list is derived from the equivalent element in the original list. The format is:

Screenshot of the code below
new_list = [expression for elem in list]

The expression is evaluated for every element in the original list, and its result defines the new list elements.

Example: Creating a list of squares

Screenshot of the code below
list  = [1,2,3,4]
new_list  = [elem * elem for elem in list]
print(new_list)

Output:

[1, 4, 9, 16]

Example: Converting strings to uppercase

Screenshot of the code below
names  = ["Noah", "Kate", "Yun"]
new_list  = [elem.upper() for elem in names]
print(new_list)

Output:

['NOAH', 'KATE', 'YUN']

Example: Extracting an element from a tuple

Screenshot of the code below
results  = [("Noah",56),("Kate",78),("Yun",69)]
marks  = [elem[1] for elem in results]
print(marks)

Output:

[56, 78, 69]

C.5. Comprehensions as filters

A filter operation creates a new list by selecting specific elements from an original list. The filtered list is always shorter (or the same length) as the original.

Using an if-clause at the end of a comprehension creates the filter condition.

Example: Selecting high numbers in a list

Screenshot of the code below
marks  = [83,56,32,74,92,32]
high  = [elem for elem in marks if elem > 70]
print(high)

Output:

[83, 74, 92]

Example: Filtering out invalid values

Screenshot of the code below
names  = ["Noah","A","M","Kate","Yun",""]
valid_names  = [name for name in names if len(name) > 1]
print(valid_names)

Output:

['Noah', 'Kate', 'Yun']

Example: Filtering on a value in a tuple

Screenshot of the code below
results  = [("Noah",24),("Kate",78),("Yun",69)]
passed  = [elem for elem in results if elem[1] > 40]
print(passed)

Output:

[('Kate', 78), ('Yun', 69)]

C.6. Combining map and filter

A mapping expression and a filter condition can be used at the same time in a comprehension.

Example: Filtering and selecting from a tuple

Screenshot of the code below
results  = [("Noah",24),("Kate",78),("Yun",69)]
passed  = [elem[0] for elem in results if elem[1] > 40]
print(passed)

Output:

['Kate', 'Yun']

Example: Finding positives and rounding

Screenshot of the code below
readings  = [83.44,6.0034,-0.223,-10.9997,4.998,-2.5]
pos  = [round(num,1) for num in readings if num > 0]
print(pos)

Output:

[83.4, 6.0, 5.0]

C.7. Checking conditions for lists: any / all

The all function checks whether all elements in a list are True:

Screenshot of the code below
list = [True, True, False]
print(all(list))

Output:

False
Screenshot of the code below
list = [True, True, True]
print(all(list))

Output:

True

This can be combined with a mapping operation to check a condition for all elements in a list. This is done by using a boolean expression for the mapping to create a list of boolean values, and then applying the all function.

Example: Check whether all values are positive

Screenshot of the code below
readings  = [83.44,6.0034,-0.223,-10.9997,4.998,-2.5]
print(all([num > 0 for num in readings]))

Output:

False

Example: Check whether all strings in a list consist only of digits

Screenshot of the code below
numbers  = ["83","56","32000","0","1"]
print(all([num.isdigit() for num in numbers]))

Output:

True

A similar test can be done with the function any, which returns True if any elements in its parameter list are True.

Example: Check for missing values

Screenshot of the code below
numbers  = ["83",None,"56",None,"32000","0","1"]
has_missing_data  = any([num is None for num in numbers])
print(has_missing_data)

Output:

True

C.8. Conditional mapping

The mapping comprehension described above can also be extended to map to two different expressions depending on a condition. This is achieved by writing an if-else expression before the for-expression:

Screenshot of the code below
new_list = [true_expr if conditional else false_expr for member in list]

Example: Replacing missing values with default value

Screenshot of the code below
names  = ["Sara", None, "Yang", "", "Michael", "Zoe"]
cleaned  = [name if name is not None else "UNKNOWN" for name in names]
print(cleaned)

Output:

['Sara', 'UNKNOWN', 'Yang', '', 'Michael', 'Zoe']

For the example above, we can also make use of the "truthyness" of a string, which would detect both None and the empty string as missing data:

Example: Replacing missing values with default value (version 2)

Screenshot of the code below
names  = ["Sara", None, "Yang", "", "Michael", "Zoe"]
cleaned  = [name if name else "UNKNOWN" for name in names]
print(cleaned)

Output:

['Sara', 'UNKNOWN', 'Yang', 'UNKNOWN', 'Michael', 'Zoe']

C.9. Comprehensions for creating lists

Comprehensions can also be used to create lists, if the list elements follow a fixed pattern.

Example: Creating a list of squares

Screenshot of the code below
squares = [x *x for x in range(6)]
print(squares)

Output:

[0, 1, 4, 9, 16, 25]