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.
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:
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]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']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']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']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!
[x for x in list if condition] - if at the end[x if condition else y for x in list] - if-else before for1# 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 negatedYou 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]]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']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'}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}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': {...}}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}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 oneWhen to use generators:
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 numbers1# 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)Use a comprehension when:
Use a traditional loop when:
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)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?
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)]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)Create a "Species Analysis System":
In this lesson you learned:
Before moving on:
if and if-else in comprehensionsPythonic 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! ⏱️🚀