34. Comprensiones: una forma compacta de crear listas, diccionarios y conjuntos
Las comprensiones son una de las características más elegantes de Python, ya que te permiten crear y transformar colecciones en una sola línea de código legible. En lugar de escribir varias líneas con bucles(loop) y operaciones append, las comprensiones te permiten expresar la misma lógica de forma más concisa y, a menudo, más clara.
En este capítulo, exploraremos cómo usar comprensiones de listas (list comprehensions), comprensiones de diccionarios (dictionary comprehensions) y comprensiones de conjuntos (set comprehensions) para escribir código más pythónico. Veremos cómo incorporar lógica condicional, cuándo elegir comprensiones en lugar de bucles tradicionales y cómo manejar escenarios más complejos con iteraciones anidadas.
34.1) Comprensiones de listas para crear y transformar listas
34.1.1) La sintaxis básica de la comprensión de listas
Una comprensión de listas (list comprehension) ofrece una forma compacta de crear una nueva lista aplicando una expresión a cada elemento de una secuencia existente. La sintaxis básica es:
[expression for item in iterable]Esto crea una nueva lista donde cada elemento es el resultado de evaluar expression para cada item en el iterable (cualquier secuencia sobre la que puedas iterar, como una lista, un rango o una cadena).
Empecemos con un ejemplo simple. Supongamos que queremos crear una lista de cuadrados para los números del 0 al 4:
# Enfoque tradicional con un bucle
squares = []
for number in range(5):
squares.append(number ** 2)
print(squares) # Output: [0, 1, 4, 9, 16]Con una comprensión de listas, podemos expresar esto de forma más concisa:
# Usando una comprensión de listas
squares = [number ** 2 for number in range(5)]
print(squares) # Output: [0, 1, 4, 9, 16]Ambos enfoques producen el mismo resultado, pero la comprensión es más compacta y, una vez que estás familiarizado con la sintaxis, a menudo es más fácil de leer. La comprensión muestra claramente que estamos creando una lista de valores al cuadrado.
34.1.2) Transformar datos existentes
Las comprensiones de listas destacan al transformar datos de una forma a otra. Veamos algunos ejemplos prácticos.
Convertir temperaturas de Celsius a Fahrenheit:
# Datos de temperatura en Celsius
celsius_temps = [0, 10, 20, 30, 40]
# Convertir a Fahrenheit usando la fórmula: F = C * 9/5 + 32
fahrenheit_temps = [temp * 9/5 + 32 for temp in celsius_temps]
print(fahrenheit_temps) # Output: [32.0, 50.0, 68.0, 86.0, 104.0]Convertir cadenas a mayúsculas:
# Códigos de producto en mayúsculas/minúsculas mezcladas
product_codes = ["abc123", "def456", "ghi789"]
# Estandarizar a mayúsculas
uppercase_codes = [code.upper() for code in product_codes]
print(uppercase_codes) # Output: ['ABC123', 'DEF456', 'GHI789']34.1.3) Crear listas a partir de objetos range
Las comprensiones de listas funcionan de forma natural con range(), que aprendimos en el Capítulo 12. Esto es útil para generar secuencias con patrones específicos:
# Generar números pares del 0 al 10
evens = [n * 2 for n in range(6)] # n va de 0 a 5, así que n*2 da 0, 2, 4, 6, 8, 10
print(evens) # Output: [0, 2, 4, 6, 8, 10]
# Generar múltiplos de 5
multiples_of_five = [n * 5 for n in range(1, 6)]
print(multiples_of_five) # Output: [5, 10, 15, 20, 25]34.1.4) Comprensiones vs construir listas con append
Es importante entender que las comprensiones de listas crean la lista completa en una sola operación, mientras que el enfoque tradicional con bucle construye la lista de forma incremental. Ambos producen el mismo resultado, pero las comprensiones suelen ser más rápidas para crear listas nuevas y se consideran más pythónicas.
Aquí tienes una comparación lado a lado:
# Enfoque tradicional con bucle
result = []
for i in range(5):
result.append(i * 3)
print(result) # Output: [0, 3, 6, 9, 12]
# Enfoque con comprensión de listas
result = [i * 3 for i in range(5)]
print(result) # Output: [0, 3, 6, 9, 12]Ambos enfoques son válidos, pero la comprensión es más concisa y expresa claramente la intención: "crear una lista de valores donde cada valor sea i * 3".
34.2) Lógica condicional dentro de comprensiones de listas
34.2.1) Filtrar con condiciones if
Una de las características más potentes de las comprensiones de listas es la capacidad de filtrar elementos según una condición. Puedes añadir una cláusula if al final de la comprensión para incluir solo los elementos que cumplan ciertos criterios:
[expression for item in iterable if condition]La cláusula if actúa como un filtro: Python evalúa la condición para cada elemento, y solo los elementos para los que la condición sea True se incluirán en la lista resultante. Los elementos que no cumplan la condición se omiten por completo.
Veamos esto en acción con un ejemplo simple:
# Obtener solo los números pares del 0 al 9
numbers = range(10)
evens = [n for n in numbers if n % 2 == 0]
print(evens) # Output: [0, 2, 4, 6, 8]Aquí, n % 2 == 0 comprueba si un número es par. Solo los números que pasan esta prueba se incluyen en la nueva lista.
Filtrar calificaciones de estudiantes:
# Puntuaciones de exámenes de estudiantes
scores = [45, 78, 92, 65, 88, 55, 73, 95]
# Obtener solo las puntuaciones aprobatorias (>= 70)
passing_scores = [score for score in scores if score >= 70]
print(passing_scores) # Output: [78, 92, 88, 73, 95]34.2.2) Transformar elementos filtrados
Puedes combinar filtrado con transformación aplicando una expresión a los elementos filtrados:
# Puntuaciones de estudiantes
scores = [45, 78, 92, 65, 88, 55, 73, 95]
# Obtener las puntuaciones aprobatorias y escalarlas al rango 0-10
scaled_passing = [score / 10 for score in scores if score >= 70]
print(scaled_passing) # Output: [7.8, 9.2, 8.8, 7.3, 9.5]
# Primero filtra (mantiene solo >= 70), luego transforma (divide entre 10)Convertir y filtrar cadenas:
# Nombres de productos con calidad mixta
products = ["apple", "BANANA", "cherry", "DATE", "elderberry"]
# Obtener versiones en mayúsculas de productos con nombres de más de 5 caracteres
long_products_upper = [product.upper() for product in products if len(product) > 5]
print(long_products_upper) # Output: ['BANANA', 'CHERRY', 'ELDERBERRY']34.2.3) Usar expresiones condicionales (if-else) en comprensiones
A veces quieres transformar elementos de forma diferente según una condición, en lugar de filtrarlos. Para esto, usas una expresión condicional (que aprendimos en el Capítulo 10) en la parte de expresión de la comprensión:
[expression_if_true if condition else expression_if_false for item in iterable]Esto es diferente del filtrado. Aquí, cada elemento se incluye en el resultado: el if-else determina qué expresión aplicar a cada elemento. La expresión condicional (del Capítulo 10) aparece en la parte de expresión, antes de la cláusula for.
Observa la diferencia de sintaxis:
- Filtrado:
[expr for item in seq if condition]-ifal final, sinelse - Expresión condicional:
[expr_if if cond else expr_else for item in seq]-if-elseen la expresión, antes defor
# Clasificar números como pares o impares
numbers = range(6)
classifications = ["even" if n % 2 == 0 else "odd" for n in numbers]
print(classifications) # Output: ['even', 'odd', 'even', 'odd', 'even', 'odd']Aplicar transformaciones diferentes según condiciones:
# Puntuaciones de estudiantes
scores = [45, 78, 92, 65, 88, 55, 73, 95]
# Añadir puntos extra a las puntuaciones suspensas, mantener las aprobatorias tal cual
adjusted_scores = [score + 10 if score < 70 else score for score in scores]
print(adjusted_scores) # Output: [55, 78, 92, 75, 88, 65, 73, 95]En ambos ejemplos, observa que:
- Cada elemento de la lista original aparece en el resultado
- El
if-elsedetermina en qué valor se convierte cada elemento - No se filtra ningún elemento
34.2.4) Comprender la diferencia: filtrado vs expresión condicional
Es crucial entender la diferencia entre estos dos patrones:
Filtrado (if al final) - Se excluyen algunos elementos:
# Incluir solo números positivos
numbers = [-2, 5, -1, 8, 0, 3]
positives = [n for n in numbers if n > 0]
print(positives) # Output: [5, 8, 3]
print(len(positives)) # Output: 3 (solo 3 elementos)
# Proceso: Comprobar condición → Si True, incluir elemento → Si False, omitir elementoExpresión condicional (if-else en la expresión) - Se incluyen todos los elementos, pero se transforman de forma diferente:
# Convertir números negativos a cero, mantener números positivos
numbers = [-2, 5, -1, 8, 0, 3]
non_negatives = [n if n > 0 else 0 for n in numbers]
print(non_negatives) # Output: [0, 5, 0, 8, 0, 3]
print(len(non_negatives)) # Output: 6 (los 6 elementos)
# Proceso: Comprobar condición → Si True, usar la primera expr → Si False, usar la segunda expr → Incluir siempre el resultado34.3) Comprensiones de diccionarios
34.3.1) Sintaxis básica de la comprensión de diccionarios
Así como las comprensiones de listas crean listas, las comprensiones de diccionarios (dictionary comprehensions) crean diccionarios. La sintaxis es similar, pero especificas tanto una clave como un valor:
{key_expression: value_expression for item in iterable}Esto crea un diccionario nuevo donde cada par clave-valor se genera a partir del iterable.
Empecemos con un ejemplo simple que crea un diccionario que asigna números a sus cuadrados:
# Crear un diccionario de números y sus cuadrados
squares_dict = {n: n ** 2 for n in range(5)}
print(squares_dict) # Output: {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}Crear un diccionario a partir de dos listas:
# Nombres de estudiantes y sus puntuaciones
names = ["Alice", "Bob", "Charlie"]
scores = [85, 92, 78]
# Crear un diccionario que asigna nombres a puntuaciones
student_scores = {names[i]: scores[i] for i in range(len(names))}
print(student_scores) # Output: {'Alice': 85, 'Bob': 92, 'Charlie': 78}Una forma más elegante de combinar dos secuencias es usando zip(), que aprenderemos en el Capítulo 37. Por ahora, el enfoque basado en índices funciona bien.
34.3.2) Transformar diccionarios existentes
Las comprensiones de diccionarios son excelentes para transformar diccionarios existentes. Puedes modificar claves, valores o ambos.
Cuando iteras sobre un diccionario en una comprensión, usa .items() para acceder tanto a claves como a valores. El método .items() devuelve pares clave-valor que puedes desempaquetar en la cláusula for:
# Precios originales en dólares
prices = {"apple": 1.50, "banana": 0.75, "cherry": 2.00}
# Convertir a centavos (multiplicar por 100)
prices_in_cents = {fruit: price * 100 for fruit, price in prices.items()}
print(prices_in_cents) # Output: {'apple': 150.0, 'banana': 75.0, 'cherry': 200.0}Transformar claves:
# Códigos de producto en minúsculas
codes = {"abc": 100, "def": 200, "ghi": 300}
# Convertir claves a mayúsculas
uppercase_codes = {code.upper(): quantity for code, quantity in codes.items()}
print(uppercase_codes) # Output: {'ABC': 100, 'DEF': 200, 'GHI': 300}Transformar tanto claves como valores:
# Nombres de estudiantes y puntuaciones
scores = {"alice": 85, "bob": 92, "charlie": 78}
# Poner en mayúscula la primera letra de los nombres y escalar las puntuaciones al rango 0-10
formatted_scores = {name.capitalize(): score / 10 for name, score in scores.items()}
print(formatted_scores) # Output: {'Alice': 8.5, 'Bob': 9.2, 'Charlie': 7.8}34.3.3) Filtrar elementos de un diccionario
Al igual que las comprensiones de listas, las comprensiones de diccionarios pueden incluir condiciones para filtrar elementos:
# Puntuaciones de estudiantes
scores = {"Alice": 85, "Bob": 65, "Charlie": 92, "David": 55, "Eve": 78}
# Obtener solo las puntuaciones aprobatorias (>= 70)
passing_scores = {name: score for name, score in scores.items() if score >= 70}
print(passing_scores) # Output: {'Alice': 85, 'Charlie': 92, 'Eve': 78}Filtrar por características de la clave:
# Inventario de productos
inventory = {"apple": 50, "banana": 30, "apricot": 20, "cherry": 40}
# Obtener solo productos que empiezan con 'a'
a_products = {product: quantity for product, quantity in inventory.items()
if product.startswith('a')}
print(a_products) # Output: {'apple': 50, 'apricot': 20}34.3.4) Crear diccionarios a partir de secuencias
Las comprensiones de diccionarios son útiles para crear diccionarios de búsqueda a partir de secuencias:
# Lista de palabras
words = ["python", "java", "ruby", "javascript"]
# Crear un diccionario que asigna cada palabra a su longitud
word_lengths = {word: len(word) for word in words}
print(word_lengths) # Output: {'python': 6, 'java': 4, 'ruby': 4, 'javascript': 10}34.3.5) Usar expresiones condicionales en comprensiones de diccionarios
Puedes usar expresiones condicionales para calcular valores de manera diferente según las condiciones:
# Puntuaciones de estudiantes
scores = {"Alice": 85, "Bob": 65, "Charlie": 92, "David": 55}
# Añadir estado de "Pass" o "Fail"
scores_with_status = {name: "Pass" if score >= 70 else "Fail"
for name, score in scores.items()}
print(scores_with_status) # Output: {'Alice': 'Pass', 'Bob': 'Fail', 'Charlie': 'Pass', 'David': 'Fail'}Aplicar transformaciones diferentes:
# Precios de productos
prices = {"apple": 1.50, "banana": 0.75, "cherry": 2.50}
# Aplicar descuento a artículos caros (> $2.00)
discounted_prices = {product: price * 0.9 if price > 2.00 else price
for product, price in prices.items()}
print(discounted_prices) # Output: {'apple': 1.5, 'banana': 0.75, 'cherry': 2.25}34.4) Comprensiones de conjuntos
34.4.1) Sintaxis básica de la comprensión de conjuntos
Las comprensiones de conjuntos (set comprehensions) crean conjuntos usando una sintaxis similar a las comprensiones de listas, pero con llaves:
{expression for item in iterable}El resultado es un conjunto, lo que significa que los valores duplicados se eliminan automáticamente y el orden no está garantizado.
# Crear un conjunto de cuadrados
squares_set = {n ** 2 for n in range(6)}
print(squares_set) # Output: {0, 1, 4, 9, 16, 25}La diferencia clave con las comprensiones de listas es que los conjuntos eliminan automáticamente los duplicados:
# Comprensión de listas: mantiene duplicados
numbers = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
squared_list = [n ** 2 for n in numbers]
print(squared_list) # Output: [1, 4, 4, 9, 9, 9, 16, 16, 16, 16]
# Comprensión de conjuntos: elimina duplicados
squared_set = {n ** 2 for n in numbers}
print(squared_set) # Output: {16, 1, 4, 9} (order may vary)Ten en cuenta que el orden de salida del conjunto puede diferir de lo que ves aquí. Los conjuntos son colecciones no ordenadas, así que Python puede mostrar los elementos en cualquier orden.
34.4.2) Extraer valores únicos
Las comprensiones de conjuntos son perfectas cuando necesitas extraer valores únicos de una colección:
# Respuestas de estudiantes (con duplicados)
responses = ["yes", "no", "yes", "maybe", "no", "yes", "maybe"]
# Obtener respuestas únicas
unique_responses = {response for response in responses}
print(unique_responses) # Output: {'maybe', 'yes', 'no'}Extraer caracteres únicos de cadenas:
# Texto con caracteres repetidos
text = "mississippi"
# Obtener caracteres únicos
unique_chars = {char for char in text}
print(unique_chars) # Output: {'m', 'i', 's', 'p'}34.4.3) Transformar y filtrar con comprensiones de conjuntos
Como otras comprensiones, las comprensiones de conjuntos pueden incluir transformaciones y condiciones:
# Nombres de estudiantes
names = ["Alice", "bob", "CHARLIE", "david", "EVE"]
# Obtener letras iniciales únicas en mayúsculas
first_letters = {name[0].upper() for name in names}
print(first_letters) # Output: {'A', 'B', 'C', 'D', 'E'}Filtrar con condiciones:
# Números con duplicados
numbers = [1, -2, 3, -4, 5, -2, 3, 6, -4]
# Obtener números positivos únicos
positive_numbers = {n for n in numbers if n > 0}
print(positive_numbers) # Output: {1, 3, 5, 6}34.4.4) Cuándo las comprensiones de conjuntos son más útiles
Las comprensiones de conjuntos son especialmente valiosas cuando:
- Necesitas valores únicos: Elimina duplicados automáticamente
- El orden no importa: Los conjuntos no están ordenados, así que úsalos cuando la secuencia no sea importante
- Vas a realizar operaciones de conjuntos: El resultado se puede usar con unión, intersección, etc. (como aprendimos en el Capítulo 17)
# Inscripciones de estudiantes en dos cursos
course_a = ["Alice", "Bob", "Charlie", "David"]
course_b = ["Charlie", "David", "Eve", "Frank"]
# Obtener estudiantes únicos entre ambos cursos usando una comprensión de conjuntos
all_students = {student for course in [course_a, course_b] for student in course}
print(all_students) # Output: {'Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank'}34.5) Elegir entre comprensiones y bucles
34.5.1) Cuándo las comprensiones son mejores
En general, se prefieren las comprensiones cuando estás creando una nueva colección transformando o filtrando una existente. Son más concisas, a menudo más legibles y normalmente más rápidas que los bucles equivalentes.
Las comprensiones destacan cuando:
- Creas una colección nueva a partir de una existente:
# Buen uso de una comprensión
prices = [10.99, 25.50, 8.75, 15.00]
discounted = [price * 0.9 for price in prices]- La transformación es directa:
# Claro y conciso
names = ["alice", "bob", "charlie"]
uppercase_names = [name.upper() for name in names]- Filtras basándote en condiciones simples:
# Fácil de entender
scores = [85, 92, 78, 65, 88, 55, 73, 95]
passing = [score for score in scores if score >= 70]34.5.2) Cuándo los bucles tradicionales son mejores
Sin embargo, hay situaciones en las que los bucles tradicionales son más apropiados y legibles:
Usa bucles cuando:
- La lógica es compleja o implica varios pasos:
# Demasiado complejo para una comprensión
results = []
for score in scores:
if score >= 90:
grade = "A"
elif score >= 80:
grade = "B"
elif score >= 70:
grade = "C"
else:
grade = "F"
results.append({"score": score, "grade": grade})Aunque podrías escribir esto como una comprensión, sería mucho más difícil de leer.
- Necesitas realizar acciones más allá de crear una colección:
# El bucle es más claro al hacer E/S o efectos secundarios
for filename in files:
with open(filename) as f:
content = f.read()
print(f"Processing {filename}")
# ... more processing- Necesitas modificar una colección existente in place:
# Modificar una lista in place: no se puede usar comprensión
numbers = [1, 2, 3, 4, 5]
for i in range(len(numbers)):
numbers[i] *= 2
print(numbers) # Output: [2, 4, 6, 8, 10]- Necesitas usar break o continue con lógica compleja:
# Encontrar la primera ocurrencia con procesamiento adicional
found = None
for item in items:
if item.startswith("target"):
found = item
print(f"Found: {found}")
break34.5.3) Consideraciones de legibilidad
El factor más importante es la legibilidad. Si una comprensión se vuelve demasiado larga o compleja, divídela en un bucle tradicional:
# Difícil de leer: pasan demasiadas cosas en una sola línea
result = [item.upper().strip() for item in items if len(item) > 5 and item.startswith('a')]
# Mejor: usa un bucle cuando la lógica sea compleja
result = []
for item in items:
if len(item) > 5 and item.startswith('a'):
cleaned = item.strip().upper()
result.append(cleaned)Una buena regla general: Si tu comprensión no cabe cómodamente en una línea (o como mucho dos líneas con un formato claro), considera usar un bucle en su lugar.
34.5.4) Consideraciones de rendimiento
Las comprensiones suelen ser más rápidas que los bucles equivalentes porque están optimizadas a nivel del intérprete. Sin embargo, esta diferencia de rendimiento normalmente es insignificante para colecciones pequeñas o medianas.
# Ambos producen el mismo resultado
# La comprensión es ligeramente más rápida
squares_comp = [n ** 2 for n in range(1000)]
# El bucle es ligeramente más lento pero más flexible
squares_loop = []
for n in range(1000):
squares_loop.append(n ** 2)Para la mayoría de los propósitos prácticos, elige basándote en la legibilidad en lugar del rendimiento. Optimiza para velocidad solo si el profiling muestra que una operación en particular es un cuello de botella.
34.5.5) Combinar enfoques
A veces, la mejor solución combina ambos enfoques:
# Usar una comprensión para una transformación simple
student_data = [
{"name": "Alice", "score": 85},
{"name": "Bob", "score": 92},
{"name": "Charlie", "score": 78}
]
# Extraer puntuaciones con una comprensión
scores = [student["score"] for student in student_data]
# Usar un bucle para procesamiento complejo
for student in student_data:
score = student["score"]
if score >= 90:
print(f"{student['name']}: Excellent!")
elif score >= 80:
print(f"{student['name']}: Good job!")
else:
print(f"{student['name']}: Keep working!")34.6) Bucles anidados y múltiples cláusulas for
34.6.1) Comprender múltiples cláusulas for
Las comprensiones pueden incluir múltiples cláusulas for, lo cual es equivalente a bucles anidados. La sintaxis es:
[expression for item1 in iterable1 for item2 in iterable2]Esto es equivalente a:
result = []
for item1 in iterable1:
for item2 in iterable2:
result.append(expression)El punto clave es que las cláusulas for se leen de izquierda a derecha, igual que los bucles anidados se escriben de arriba hacia abajo.
Empecemos con un ejemplo simple que crea todas las combinaciones de dos listas:
# Dos listas de valores
colors = ["red", "blue"]
sizes = ["S", "M", "L"]
# Crear todas las combinaciones
combinations = [(color, size) for color in colors for size in sizes]
print(combinations)
# Output: [('red', 'S'), ('red', 'M'), ('red', 'L'), ('blue', 'S'), ('blue', 'M'), ('blue', 'L')]Esto crea cada emparejamiento posible de un color con una talla.
34.6.2) Crear pares de coordenadas
Un caso de uso común es generar pares de coordenadas:
# Crear una cuadrícula 3x3 de coordenadas
coordinates = [(x, y) for x in range(3) for y in range(3)]
print(coordinates)
# Output: [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]Crear una tabla de multiplicar:
# Generar pares de multiplicación
products = [(x, y, x * y) for x in range(1, 4) for y in range(1, 4)]
for x, y, product in products:
print(f"{x} × {y} = {product}")
# Output:
# 1 × 1 = 1
# 1 × 2 = 2
# 1 × 3 = 3
# 2 × 1 = 2
# 2 × 2 = 4
# 2 × 3 = 6
# 3 × 1 = 3
# 3 × 2 = 6
# 3 × 3 = 934.6.3) Aplanar listas anidadas
Múltiples cláusulas for son útiles para aplanar estructuras anidadas:
# Lista anidada de números
nested_numbers = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# Aplanar en una sola lista
flat = [num for sublist in nested_numbers for num in sublist]
print(flat) # Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]Esto es equivalente a:
flat = []
for sublist in nested_numbers:
for num in sublist:
flat.append(num)Aplanar una lista de palabras en caracteres:
# Lista de palabras
words = ["cat", "dog", "bird"]
# Obtener todos los caracteres de todas las palabras
all_chars = [char for word in words for char in word]
print(all_chars) # Output: ['c', 'a', 't', 'd', 'o', 'g', 'b', 'i', 'r', 'd']34.6.4) Añadir condiciones a comprensiones anidadas
Puedes añadir condiciones para filtrar los resultados:
# Crear pares donde la suma sea par
pairs = [(x, y) for x in range(5) for y in range(5) if (x + y) % 2 == 0]
print(pairs)
# Output: [(0, 0), (0, 2), (0, 4), (1, 1), (1, 3), (2, 0), (2, 2), (2, 4), (3, 1), (3, 3), (4, 0), (4, 2), (4, 4)]Encontrar elementos comunes entre listas:
# Dos listas de números
list1 = [1, 2, 3, 4, 5]
list2 = [4, 5, 6, 7, 8]
# Encontrar pares donde los valores sean iguales (elementos comunes)
common = [x for x in list1 for y in list2 if x == y]
print(common) # Output: [4, 5]Nota: Para encontrar elementos comunes, usar la intersección de conjuntos es más eficiente: set(list1) & set(list2), que aprendimos en el Capítulo 17.
34.6.5) Comprensiones de diccionarios anidadas
También puedes usar múltiples cláusulas for en comprensiones de diccionarios:
# Crear un diccionario de sumas de coordenadas
coord_sums = {(x, y): x + y for x in range(3) for y in range(3)}
print(coord_sums)
# Output: {(0, 0): 0, (0, 1): 1, (0, 2): 2, (1, 0): 1, (1, 1): 2, (1, 2): 3, (2, 0): 2, (2, 1): 3, (2, 2): 4}34.6.6) Cuándo evitar comprensiones anidadas
Aunque las comprensiones anidadas son potentes, pueden volverse difíciles de leer rápidamente. Considera estas pautas:
Aceptable - relativamente simple:
# Dos niveles de anidación, expresión simple
matrix = [[i * j for j in range(3)] for i in range(3)]
print(matrix) # Output: [[0, 0, 0], [0, 1, 2], [0, 2, 4]]Se complica: considera un bucle:
# Tres niveles de anidación: difícil de leer
result = [[[i + j + k for k in range(2)] for j in range(2)] for i in range(2)]
# Mejor como bucles anidados para mayor claridadRegla general: Si tienes más de dos cláusulas for, o si la expresión es compleja, usa bucles anidados tradicionales en su lugar:
# Más claro con bucles explícitos
result = []
for i in range(2):
middle = []
for j in range(2):
inner = []
for k in range(2):
inner.append(i + j + k)
middle.append(inner)
result.append(middle)Las comprensiones con múltiples cláusulas for son herramientas potentes, pero recuerda: la claridad es más importante que la brevedad. Si una comprensión anidada se vuelve difícil de entender, es mejor usar bucles explícitos.