Python Comprehensions

Introduction to Software Engineering (CSSE 1001)

Author

Paul Vrbik

Published

April 24, 2025

List Comprehension

List comprehension is a way rapidly build a list without requiring a for loop and an accumulator.

It is the analogue of set builder notation from mathematics. In math we do.

  1. \(\{ k^2 \mathop{:} k \in \{0,1,2,3\} \} = \{ 0, 1, 4, 9 \}\)
  2. \(\sum_{k \in \{0,1,2,3\}} k^2 = 0^2 + 1^2 + 2^2 + 3^2\)

Whereas in Python we can write.

[k**2 for k in range(4)]
[0, 1, 4, 9]
sum(k**2 for k in range(4))
14

Dictionary Comprehension

We can also rapidly build dictionaries through comprehension.

{x: f"value is {x}" for x in range(3)}
{0: 'value is 0', 1: 'value is 1', 2: 'value is 2'}

This is useful for assigning default values to keys.

{x: 0 for x in "ABC"}
{'A': 0, 'B': 0, 'C': 0}

With comprehensions we can generate a frequency dictionary with one expression.

cs = "hello world"
{x: cs.count(x) for x in cs}
{'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}

Nested Comprehensions

We can utilize multiple fors in a comprehension.

[(a, x) for a in "ABC" for x in range(2)]
[('A', 0), ('A', 1), ('B', 0), ('B', 1), ('C', 0), ('C', 1)]

This generate the same list as the following accumulation.

xs = []
for a in "ABC":
    for x in range(2):
        xs.append((a,x))
print(xs)
[('A', 0), ('A', 1), ('B', 0), ('B', 1), ('C', 0), ('C', 1)]

Consequently the output is different depending on the order you give the for loops in the comprehension.

[(a, x) for x in range(2) for a in "ABC"]
[('A', 0), ('B', 0), ('C', 0), ('A', 1), ('B', 1), ('C', 1)]

Exercise 1 (Disassemble with For Loop) Complete the function according to its specification.

def disassemble(cs: str) -> list[str]:
    """Returns a list of characters comprising cs,
    maintaining order.
    >>> disassemble("")
    []
    >>> disassemble("abcdef")
    ['a', 'b', 'c', 'd', 'e', 'f']
    """
def disassemble(xs: str) -> list[str]:
    ans = []
    for x in xs:
        ans.append(x)
    return ans
disassemble("abcdef")
['a', 'b', 'c', 'd', 'e', 'f']
def disassemble(xs: str) -> list[str]:
    return [x for x in xs]
disassemble("abcdef")
['a', 'b', 'c', 'd', 'e', 'f']

Exercise 2 Implement the following function according to its specification.

def more_capitals(cs: str) -> list[str]:
    """
    >>> more_capitals("aAbBcDd")
    False
    >>> more_capitals("CSSE 1001")
    True
    >>> more_capitals("")
    False
    """
Tip

A useful “hack”: the integer value of True is 1 and False is 0.

True + True + False
2
def more_capitals(cs: str) -> bool:
    return (sum(c.islower() for c in cs) 
            > sum(c.isupper() for c in cs))

Exercise 3 (All Truthy) Implement the following function according to its specification.

def all(xs: list) -> bool:
    """
    >>> all([])
    True
    >>> all([1, 2, 3])
    True
    >>> all(["a", "", "c"])
    False
    """
def all(xs: list) -> bool:
    """
    >>> all([])
    True
    >>> all([1, 2, 3])
    True
    >>> all(["a", "", "c"])
    False
    """
    return len(xs) == sum(bool(x) for x in xs)

Exercise 4 (Any Truthy) Implement the function according to its specification.

def any(xs: list) -> bool:
    """
    >>> any([])
    False
    >>> any([0, 0, 0])
    False
    >>> any(["a", "", "c"])
    True
    """

Implement the following function according to its specification.

def any(xs: list) -> bool:
    """
    >>> any([])
    False
    >>> any([0, 0, 0])
    False
    >>> any(["a", "", "c"])
    True
    """
    return sum(bool(x) for x in xs) > 0

All and Any in Python

The functions all and any are included in base Python (that is, no library call is necessary to use them).

They are useful. For example we can write a simple expression that checks if

xs = "HELLOWORLD"

is comprised of only capital letters:

all(['A' < x < 'Z' for x in xs])
True

Or ask if there are any lower case letters (there are not):

any(['a' < x < 'z' for x in xs])

List Comprehension Filtering

The elements of a list-comprehension may be filtered by specifying a requirement for an item to be included.

For example we generate the the numbers less than one hundred that are divisible both by three and seven by the following list comprehension.

[x for x in range(101) if not (x % 3) and not (x % 7)]
[0, 21, 42, 63, 84]

General Pattern

The general pattern for a list comprehension is:

[x for x in xs if P(x)]
[x for x in xs for y in ys if P(x, y)]
[x for x in xs for y in ys for z in zs if P(x, y, z)]
...

where P is some predicate.

Summary

Comprehensions are a rapid way of forming lists and dictionaries that would otherwise require accumulation with a for-loop.

Tons of practice.