We use cookies to enhance your experience on the site
CodeWorlds

List Comprehensions - Efficient Exploration

Welcome back, @name! 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# ['Tiger', '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', 'hot']
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 10 numbers

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

Create a "Species Analysis System":

  1. A list of 20 random species with data (name, size 0.1-5.0m, dangerous T/F)
  2. Use comprehensions to:
    • Dictionary: name → data
    • List of only dangerous species
    • Dictionary: size category → list of species
    • Average size for each category
    • Top 5 largest species

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 introduce you to algorithm complexity - how to measure the pace of exploration! ⏱️🚀

Go to CodeWorlds