Python course Β· Module 2: Data Structures

List Comprehensions - Efficient Exploration

14 min read
In this lesson15

Welcome back! Darwin here with another powerful exploration tool.

In the previous lessons you learned about lists and dictionaries. You've already seen previews of comprehensions - a way to create collections in one concise line. Now it's time to dive deep into this topic!

Comprehensions are like a treasure map - instead of describing step by step how to reach the destination (traditional loops), you express directly what you're looking for (comprehension). This is one of the most "Pythonic" features of the language - code becomes more readable, shorter, and often faster.

What Are Comprehensions?

A comprehension is Python syntax that allows you to create collections (lists, dictionaries, sets) in one line, instead of using loops.

1# Traditional way (4 lines)
2squares = []
3for x in range(1, 6):
4    squares.append(x ** 2)
5# [1, 4, 9, 16, 25]
6
7# List comprehension (1 line!)
8squares = [x ** 2 for x in range(1, 6)]
9# [1, 4, 9, 16, 25]

Advantages of comprehensions:

  • Shorter - 1 line instead of 3-4
  • More readable - they express intent directly
  • Faster - internally optimized by Python
  • Pythonic - idiomatic code style

List Comprehensions - Basics

Syntax: [expression for element in iterable]

1# Example 1: Numbers from 0 to 9
2numbers = [x for x in range(10)]
3# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
4
5# Example 2: Squares of numbers
6squares = [x ** 2 for x in range(1, 11)]
7# [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
8
9# Example 3: Temperature conversion
10fahrenheit_temps = [32, 68, 104, 212]
11celsius_temps = [(f - 32) * 5/9 for f in fahrenheit_temps]
12# [0.0, 20.0, 40.0, 100.0]
13
14# Example 4: Uppercase
15animals = ["tiger", "elephant", "parrot"]
16animals_upper = [animal.upper() for animal in animals]
17# ["TIGER", "ELEPHANT", "PARROT"]
18
19# Example 5: Word lengths
20species = ["Python", "Elephas", "Leo"]
21lengths = [len(s) for s in species]
22# [6, 7, 3]

Safari Example - Discovered Species Names

1# We have a list of species (scientific names)
2discovered = ["python regius", "panthera leo", "loxodonta africana"]
3
4# We want to format them: Uppercase + replace spaces with _
5formatted = [name.upper().replace(" ", "_") for name in discovered]
6# ['PYTHON_REGIUS', 'PANTHERA_LEO', 'LOXODONTA_AFRICANA']
7
8# Or add a "SPECIES_" prefix
9prefixed = [f"SPECIES_{name.upper()}" for name in discovered]
10# ['SPECIES_PYTHON REGIUS', 'SPECIES_PANTHERA LEO', 'SPECIES_LOXODONTA AFRICANA']

List Comprehensions with Conditions

Syntax: [expression for element in iterable if condition]

1# Example 1: Only even numbers
2numbers = [x for x in range(1, 11) if x % 2 == 0]
3# [2, 4, 6, 8, 10]
4
5# Example 2: Only odd numbers
6odd_numbers = [x for x in range(1, 11) if x % 2 != 0]
7# [1, 3, 5, 7, 9]
8
9# Example 3: Only positive numbers
10temperatures = [-5, 3, -2, 10, 15, -1, 8]
11positive = [t for t in temperatures if t > 0]
12# [3, 10, 15, 8]
13
14# Example 4: Only long words (>5 characters)
15animals = ["Tiger", "Lion", "Parrot", "Python", "Leo"]
16long_names = [a for a in animals if len(a) > 5]
17# ['Parrot', 'Python']
18
19# Example 5: Only words starting with 'P'
20p_animals = [a for a in animals if a.startswith('P')]
21# ['Parrot', 'Python']

Safari Example - Filtering Discoveries

1# List of discovered species with danger info
2species_data = [
3    {"name": "Python regius", "dangerous": False, "size": 1.5},
4    {"name": "Panthera leo", "dangerous": True, "size": 2.5},
5    {"name": "Elephas maximus", "dangerous": False, "size": 3.0},
6    {"name": "Crocodylus niloticus", "dangerous": True, "size": 4.5}
7]
8
9# Only dangerous species
10dangerous = [s["name"] for s in species_data if s["dangerous"]]
11# ['Panthera leo', 'Crocodylus niloticus']
12
13# Only large species (>2.5m)
14large = [s["name"] for s in species_data if s["size"] > 2.5]
15# ['Elephas maximus', 'Crocodylus niloticus']
16
17# Only safe and small (<2m)
18safe_small = [s["name"] for s in species_data
19              if not s["dangerous"] and s["size"] < 2.0]
20# ['Python regius']

If-Else in List Comprehensions

Syntax: [expression_if if condition else expression_else for element in iterable]

1# Example 1: Even/Odd labels
2numbers = [1, 2, 3, 4, 5]
3labels = ["even" if x % 2 == 0 else "odd" for x in numbers]
4# ['odd', 'even', 'odd', 'even', 'odd']
5
6# Example 2: Temperature classification
7temps = [15, 25, 35, 10, 30]
8classification = ["hot" if t > 30 else "normal" if t > 20 else "cold"
9                  for t in temps]
10# ['cold', 'normal', 'hot', 'cold', 'normal']
11
12# Example 3: Value conversion
13raw_data = [5, 0, 10, -1, 15, 0, 20]
14cleaned = [x if x > 0 else 0 for x in raw_data]
15# [5, 0, 10, 0, 15, 0, 20]

NOTE: The if-else syntax is different from just if!

  • Filtering: [x for x in list if condition] - if at the end
  • Transformation: [x if condition else y for x in list] - if-else before for
1# Filtering (skips elements)
2result = [x for x in range(10) if x % 2 == 0]
3# [0, 2, 4, 6, 8] - only even
4
5# Transformation (changes elements)
6result = [x if x % 2 == 0 else -x for x in range(10)]
7# [0, -1, 2, -3, 4, -5, 6, -7, 8, -9] - even unchanged, odd negated

Nested List Comprehensions

You can nest comprehensions for complex transformations:

1# Example 1: Flattening a 2D list
2matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
3flattened = [num for row in matrix for num in row]
4# [1, 2, 3, 4, 5, 6, 7, 8, 9]
5
6# Example 2: Combinations
7colors = ["red", "green"]
8sizes = ["S", "M", "L"]
9combinations = [f"{color}_{size}" for color in colors for size in sizes]
10# ['red_S', 'red_M', 'red_L', 'green_S', 'green_M', 'green_L']
11
12# Example 3: Matrix multiplication (simplified)
13matrix_2d = [[1, 2], [3, 4], [5, 6]]
14doubled = [[num * 2 for num in row] for row in matrix_2d]
15# [[2, 4], [6, 8], [10, 12]]

Safari Example - Expedition Combinations

1# Teams and locations
2teams = ["Alpha", "Beta", "Gamma"]
3locations = ["Jungle", "Savanna", "River"]
4
5# All possible assignments
6assignments = [f"Team {team} β†’ {location}"
7               for team in teams for location in locations]
8
9# Result:
10# ['Team Alpha β†’ Jungle', 'Team Alpha β†’ Savanna', 'Team Alpha β†’ River',
11#  'Team Beta β†’ Jungle', 'Team Beta β†’ Savanna', 'Team Beta β†’ River',
12#  'Team Gamma β†’ Jungle', 'Team Gamma β†’ Savanna', 'Team Gamma β†’ River']

Dictionary Comprehensions

Syntax: {key: value for element in iterable}

1# Example 1: Squares as dictionary
2squares = {x: x ** 2 for x in range(1, 6)}
3# {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
4
5# Example 2: Word lengths
6animals = ["Tiger", "Lion", "Parrot"]
7lengths_dict = {animal: len(animal) for animal in animals}
8# {'Tiger': 5, 'Lion': 4, 'Parrot': 6}
9
10# Example 3: From two lists (zip)
11keys = ["Python", "Leo", "Elephas"]
12values = ["Python", "Lion", "Elephant"]
13translation = {k: v for k, v in zip(keys, values)}
14# {'Python': 'Python', 'Leo': 'Lion', 'Elephas': 'Elephant'}
15
16# Example 4: Reversing a dictionary
17original = {"a": 1, "b": 2, "c": 3}
18reversed_dict = {v: k for k, v in original.items()}
19# {1: 'a', 2: 'b', 3: 'c'}

Dict Comprehension with Condition

1# Only species with names longer than 5 characters
2animals = ["Tiger", "Lion", "Parrot", "Python", "Leo"]
3long_animals = {a: len(a) for a in animals if len(a) > 5}
4# {'Parrot': 6, 'Python': 6}
5
6# Only positive values
7data = {"a": 5, "b": -3, "c": 10, "d": -1, "e": 7}
8positive_only = {k: v for k, v in data.items() if v > 0}
9# {'a': 5, 'c': 10, 'e': 7}

Safari Example - Species Catalog

1# List of tuples (scientific name, common name, length)
2species_list = [
3    ("Python regius", "Royal Python", 1.5),
4    ("Panthera leo", "Lion", 2.5),
5    ("Elephas maximus", "Asian Elephant", 3.0),
6    ("Loxodonta africana", "African Elephant", 3.5)
7]
8
9# Create dictionary: scientific_name β†’ {data}
10catalog = {
11    name: {"common": common_name, "length": length}
12    for name, common_name, length in species_list
13}
14
15print(catalog["Python regius"])
16# {'common': 'Royal Python', 'length': 1.5}
17
18# Only large species (>2m)
19large_catalog = {
20    name: {"common": common_name, "length": length}
21    for name, common_name, length in species_list
22    if length > 2.0
23}
24# {'Panthera leo': {...}, 'Elephas maximus': {...}, 'Loxodonta africana': {...}}

Set Comprehensions

Syntax: {expression for element in iterable}

1# Example 1: Unique lengths
2animals = ["Tiger", "Lion", "Parrot", "Python", "Leo"]
3unique_lengths = {len(a) for a in animals}
4# {3, 4, 5, 6} - set (unordered, unique)
5
6# Example 2: First letters
7first_letters = {a[0] for a in animals}
8# {'T', 'L', 'P'}
9
10# Example 3: Even squares
11even_squares = {x ** 2 for x in range(1, 11) if x % 2 == 0}
12# {4, 16, 36, 64, 100}

Set comprehension automatically removes duplicates:

1numbers = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
2unique = {x for x in numbers}
3# {1, 2, 3, 4}
4
5# You can also do: set(numbers)
6# But set comprehension allows transformations:
7unique_doubled = {x * 2 for x in numbers}
8# {2, 4, 6, 8}

Generator Expressions

Syntax: (expression for element in iterable)

A generator expression looks like a list comprehension, but uses () instead of []. Difference: A generator doesn't create the entire list in memory at once, but generates elements "on demand".

1# List comprehension - creates the entire list
2squares_list = [x ** 2 for x in range(1000000)]  # Uses a lot of memory!
3
4# Generator expression - generates one by one
5squares_gen = (x ** 2 for x in range(1000000))  # Uses little memory!
6
7# A generator can only be iterated once
8for square in squares_gen:
9    print(square)  # Generates values one by one

When to use generators:

  • When processing large datasets
  • When you don't need the entire list at once
  • When you iterate only once
  • When you want to save memory
1# Summing squares (efficiently with generator)
2total = sum(x ** 2 for x in range(1000000))
3
4# Maximum (efficiently with generator)
5max_val = max(x ** 2 for x in range(1000))
6
7# Filtering with generator
8large_numbers = (x for x in range(1000000) if x > 999990)
9for num in large_numbers:
10    print(num)  # Prints only the last 9 numbers (999991-999999)

Safari Example - Processing Large Data

1# We have millions of discoveries in the database
2def get_all_species():
3    """Simulation of fetching millions of entries"""
4    for i in range(1000000):
5        yield {"id": i, "name": f"Species_{i}", "size": i % 100}
6
7# Bad - creates the entire list (memory-hungry!)
8# large_species = [s["name"] for s in get_all_species() if s["size"] > 90]
9
10# Good - uses a generator (memory-efficient!)
11large_species = (s["name"] for s in get_all_species() if s["size"] > 90)
12
13# We can iterate without loading everything into memory
14for species in large_species:
15    print(species)

Comprehensions vs Traditional Loops

When to Use Comprehensions?

Use a comprehension when:

  • You're transforming a simple collection
  • You're filtering elements by a simple condition
  • Code fits in 1 line (max 80-100 characters)
  • Logic is simple and readable

Use a traditional loop when:

  • Logic is complex (many if/else)
  • You need many operations on each element
  • Code becomes unreadable
  • You need exception handling (try/except)

Comparison

1# Good use of comprehension
2squares = [x ** 2 for x in range(10)]
3even = [x for x in range(20) if x % 2 == 0]
4names = [s.upper() for s in species]
5
6# Bad - too complicated for a comprehension
7result = [
8    process_complex_logic(x, y, z)
9    if check_condition_1(x) and check_condition_2(y)
10    else alternative_processing(x)
11    if check_condition_3(z)
12    else default_value
13    for x, y, z in complicated_data
14    if validate(x) and validate(y) and validate(z)
15]  # DON'T DO THIS!
16
17# Better - traditional loop
18result = []
19for x, y, z in complicated_data:
20    if not (validate(x) and validate(y) and validate(z)):
21        continue
22
23    if check_condition_1(x) and check_condition_2(y):
24        result.append(process_complex_logic(x, y, z))
25    elif check_condition_3(z):
26        result.append(alternative_processing(x))
27    else:
28        result.append(default_value)

Comprehension Performance

Comprehensions are usually faster than traditional loops!

1import time
2
3# Traditional loop
4start = time.time()
5result = []
6for x in range(1000000):
7    result.append(x ** 2)
8time_loop = time.time() - start
9
10# List comprehension
11start = time.time()
12result = [x ** 2 for x in range(1000000)]
13time_comp = time.time() - start
14
15print(f"Loop: {time_loop:.4f}s")
16print(f"Comprehension: {time_comp:.4f}s")
17print(f"Comprehension is {time_loop/time_comp:.2f}x faster!")
18
19# Typical results:
20# Loop: 0.1234s
21# Comprehension: 0.0987s
22# Comprehension is 1.25x faster!

Why are comprehensions faster?

  • Python optimizes them internally
  • Fewer function calls (e.g., append())
  • The interpreter "knows" what you're doing and can optimize

Practical Example - Expedition Analysis

1# Expedition data (day, distance km, species discovered, temperature Β°C)
2expedition_log = [
3    (1, 15, 3, 32),
4    (2, 12, 5, 31),
5    (3, 8, 2, 35),
6    (4, 20, 7, 33),
7    (5, 10, 4, 30),
8    (6, 18, 6, 34),
9    (7, 14, 3, 32)
10]
11
12# 1. Total distance
13total_distance = sum(day[1] for day in expedition_log)
14print(f"Total distance: {total_distance} km")  # 97 km
15
16# 2. Average temperature
17avg_temp = sum(day[3] for day in expedition_log) / len(expedition_log)
18print(f"Average temperature: {avg_temp:.1f}Β°C")  # 32.4Β°C
19
20# 3. Days with many discoveries (>4)
21productive_days = [day[0] for day in expedition_log if day[2] > 4]
22print(f"Productive days: {productive_days}")  # [2, 4, 6]
23
24# 4. Dictionary: day β†’ data
25daily_stats = {
26    day: {"distance": dist, "species": spec, "temp": temp}
27    for day, dist, spec, temp in expedition_log
28}
29print(daily_stats[1])  # {'distance': 15, 'species': 3, 'temp': 32}
30
31# 5. Day classification
32day_types = {
33    day: "productive" if spec > 5 else "normal" if spec > 3 else "weak"
34    for day, dist, spec, temp in expedition_log
35}
36print(day_types)
37# {1: 'weak', 2: 'normal', 3: 'weak', 4: 'productive',
38#  5: 'normal', 6: 'productive', 7: 'weak'}
39
40# 6. Only hot days (>33Β°C)
41hot_days = [(day, temp) for day, dist, spec, temp in expedition_log if temp > 33]
42print(f"Hot days: {hot_days}")  # [(3, 35), (6, 34)]

Nested Dict/List Comprehensions - Advanced

1# Example: Distance matrix between locations
2locations = ["Camp", "River", "Waterfall", "Peak"]
3
4# Create a distance matrix (random values)
5import random
6distance_matrix = {
7    loc1: {
8        loc2: random.randint(5, 50) if loc1 != loc2 else 0
9        for loc2 in locations
10    }
11    for loc1 in locations
12}
13
14print(distance_matrix["Camp"])
15# {'Camp': 0, 'River': 23, 'Waterfall': 41, 'Peak': 37}
16
17# Find all location pairs with distance <20km
18close_pairs = [
19    f"{loc1} ↔ {loc2}"
20    for loc1, distances in distance_matrix.items()
21    for loc2, dist in distances.items()
22    if 0 < dist < 20
23]
24print(close_pairs)

Practical Exercise

In the editor, fill in four lists, each with a one-line list comprehension:

  1. squares - the squares of the numbers from 1 to 10
  2. evens - the even numbers from 1 to 20 (an if condition)
  3. upper_animals - the animal names in uppercase
  4. late_alphabet - the animals whose names come after "l" in the alphabet
Code for this lesson: main.py
1# List comprehension - elegant list creation
2# Syntax: [expression for element in iterable if condition]
3
4# Regular list creation
5animal_names = ["lion", "elephant", "zebra", "cheetah", "hippo", "giraffe"]
6
7# Name lengths - traditional approach
8lengths = []
9for name in animal_names:
10    lengths.append(len(name))
11print(f"Lengths (loop): {lengths}")
12
13# The same with list comprehension!
14lengths_comp = [len(name) for name in animal_names]
15print(f"Lengths (comprehension): {lengths_comp}")
16
17# With a condition (filtering)
18long_names = [name for name in animal_names if len(name) > 4]
19print(f"Long names (>4 chars): {long_names}")
20
21# Transformation + filtering
22upper_short = [name.upper() for name in animal_names if len(name) <= 4]
23print(f"Short UPPERCASE: {upper_short}")
24
25# TODO: Create a list of squares of numbers from 1 to 10
26# squares = [? for ? in range(1, 11)]
27squares = []
28print(f"\nSquares: {squares}")
29
30# TODO: Create a list of even numbers from 1 to 20
31# evens = [? for ? in range(1, 21) if ?]
32evens = []
33print(f"Even numbers: {evens}")
34
35# TODO: Create a list of animal names in uppercase
36# upper_animals = [? for ? in animal_names]
37upper_animals = []
38print(f"Uppercase: {upper_animals}")
39
40# TODO: Create a list of animals starting with a letter after "l" (alphabetically)
41# late_alphabet = [? for ? in animal_names if ?]
42late_alphabet = []
43print(f"After 'l' in alphabet: {late_alphabet}")
44

Summary

In this lesson you learned:

  • What comprehensions are and why they're powerful
  • List comprehensions (basic and with conditions)
  • Dictionary comprehensions
  • Set comprehensions
  • Generator expressions (memory savings)
  • Nested comprehensions
  • When to use comprehensions vs loops
  • Comprehension performance

Checkpoint

Before moving on:

  • You understand list comprehension syntax
  • You can use conditions (if) in comprehensions
  • You understand the difference between if and if-else in comprehensions
  • You can create dict comprehensions
  • You know the difference between a list and a generator
  • You know when to use a comprehension and when to use a loop

Pythonic rule: "Simple is better than complex" - if a comprehension makes code unreadable, use a traditional loop!

In the next lesson Darwin will show you sorting algorithms - how to put the species catalog in order!

Spotted a mistake in this lesson?

Check yourself

Answer the questions from this lesson. Pick an answer to see right away whether it is correct.

  1. 1. What does [x*2 for x in range(3)] return?

  2. 2. What does [x for x in range(5) if x % 2 == 0] return?

Hands-on tasks in the game

  • Code editor

    Create a list squares with the squares of the numbers from 1 to 5 using a list comprehension

  • Click in order

    Click in order: [x**2 for x in range(5)]

Useful articles