Tarjetas de detalles arquitectónicos seleccionadas en un soporte y comprobadas con una lupa sobre una mesa de estudio.

Comprensiones de listas y diccionarios: transformar datos sin ocultar la intención

Hay una forma bastante eficaz de convertir cinco líneas comprensibles en una pequeña prueba de acceso al equipo: comprimirlas hasta que solo quien las escribió pueda explicar qué ocurre. Python ofrece comprensiones de listas y diccionarios, y son útiles precisamente porque pueden expresar con claridad una transformación. Utilizarlas como concurso de reducción de caracteres es una decisión del participante, no una exigencia del lenguaje. El próximo compañero no debería necesitar descifrar tu firma para mantener un filtro.

Vamos a trabajar con tres incidencias inventadas. Primero obtendremos una lista de las abiertas y comprobaremos que coincide con un bucle explícito. Después construiremos un índice por identificador y veremos qué sucede si esa identidad aparece dos veces. El ejemplo es Python puro: no consulta una API, no cambia incidencias reales y no necesita credenciales. Al terminar tendrás un criterio para elegir la forma breve y, algo más interesante, para dejar de utilizarla cuando empieza a ocultar decisiones.

Tabla de contenidos

Una lista nueva con una intención que se pueda leer

Una comprensión de listas construye una lista a partir de un recorrido y una expresión. Puede incluir una condición para decidir qué elementos entran. No es una operación exclusiva para datos BIM; nos sirve aquí porque muchas tareas consisten en preparar una selección o una representación más pequeña de una colección. Antes de escribirla deberíamos poder explicar la transformación sin referirnos a su sintaxis: queremos las incidencias abiertas, conservando su orden y mostrando identificador y título como resumen.

El tutorial de estructuras de datos de Python muestra este uso para construir resultados y seleccionar elementos. La idea también está recogida en la propuesta original de comprensiones de listas. No necesitamos aprovechar todas las combinaciones que permite la sintaxis. Un recorrido, una condición y una salida breve ya resuelven una parte bastante amplia del trabajo cotidiano.

Guarda este primer bloque en comprensiones.py. Funciona con Python 3.10 o posterior y no requiere paquetes:

				
					incidencias = [
    {"id": "I-01", "estado": "abierta", "titulo": "Revisar acceso"},
    {"id": "I-02", "estado": "cerrada", "titulo": "Comprobar cota"},
    {"id": "I-03", "estado": "abierta", "titulo": "Resolver encuentro"},
]

abiertas_bucle = []
for incidencia in incidencias:
    if incidencia["estado"] == "abierta":
        abiertas_bucle.append({
            "id": incidencia["id"],
            "resumen": incidencia["titulo"],
        })

abiertas = [
    {"id": incidencia["id"], "resumen": incidencia["titulo"]}
    for incidencia in incidencias
    if incidencia["estado"] == "abierta"
]

assert abiertas == abiertas_bucle
print("Abiertas:", abiertas)
				
			

La salida muestra I-01 e I-03 con sus resúmenes. El assert comprueba la igualdad entre las dos listas. Ambas versiones recorren la misma entrada, comparan el mismo estado y crean un diccionario nuevo para cada incidencia seleccionada. Si ya entiendes cómo funciona un bucle, la comprensión no añade una nueva regla de negocio; ofrece otra forma de escribir ese recorrido.

Se lee con tres preguntas: ¿qué voy a producir?, ¿de dónde viene cada elemento?, ¿cuál entra? La expresión inicial responde la primera; for identifica el recorrido e if establece el filtro. He repartido la comprensión en varias líneas porque facilita ver sus partes. La ventaja de una construcción compacta no exige meterla toda en una sola línea, aunque eso arruine algunas demostraciones de superioridad bastante económicas.

Filtrar y transformar siguen siendo decisiones distintas

El filtro comprueba el estado; la transformación crea los campos id y resumen. Cambiar una parte no debería confundirse con cambiar la otra. Si decidiéramos mostrar todas las incidencias con una etiqueta distinta según su estado, no estaríamos filtrando: conservaríamos elementos y cambiaríamos su representación. Es la misma distinción que desarrollamos en filtrar, transformar y agrupar, ahora aplicada a una sintaxis concreta.

El ejemplo parte de diccionarios con los campos esperados y estados conocidos. El acceso directo mediante corchetes fallará si falta un campo necesario. No lo sustituiría automáticamente por get con un texto vacío para que «no se rompa». Eso cambiaría el significado: un dato ausente podría acabar disfrazado de resumen legítimo. Si la entrada no está validada, decide primero qué hacer con ese defecto y cómo comunicarlo. El filtro de incidencias abiertas no es, por sí solo, un validador de todo el lote.

La lista resultante también tiene una relación concreta con la entrada. Aquí cada fila de salida es un diccionario nuevo y sus valores son textos. Cambiar la clave resumen de uno de esos diccionarios no altera el título del original. Pero una comprensión que seleccionase directamente los diccionarios originales conservaría referencias a ellos. Crear una lista nueva no significa copiar profundamente los objetos que contiene. En cuanto aparezcan listas anidadas u otros valores mutables, hay que volver a mirar esa frontera.

Conservar el orden tampoco significa ordenar por prioridad o fecha. I-01 aparece antes que I-03 porque estaba antes en la entrada. Si el informe necesita otra clasificación, debemos pedirla expresamente y comprobarla. Una comprensión no conoce el criterio de entrega del proyecto. Afortunadamente: ya tenemos suficientes programas que parecen tomar decisiones por interpretar de forma generosa lo que nadie les pidió.

Un diccionario puede ocultar un duplicado con una elegancia preocupante

La comprensión de diccionarios expresa una pareja clave:valor para cada elemento recorrido. La PEP de comprensiones de diccionarios presenta esa correspondencia con la construcción de listas. En nuestro caso podríamos querer consultar el estado de una incidencia a partir de su identificador. Es un buen motivo para un índice, siempre que un identificador represente una única entrada o hayamos definido cómo resolver las repeticiones.

Añade este bloque al mismo archivo, debajo del anterior. Introduce deliberadamente un segundo registro con I-01 para ver el problema:

				
					repetidas = incidencias + [
    {"id": "I-01", "estado": "cerrada", "titulo": "Otro registro"}
]

sin_comprobar = {
    incidencia["id"]: incidencia["estado"]
    for incidencia in repetidas
}
print("Sin comprobar:", sin_comprobar)
				
			

La salida contiene tres claves aunque la lista de entrada tenga cuatro registros. I-01 queda asociada a cerrada, el estado del último registro con esa clave. No salta una excepción que nos obligue a resolver el conflicto. La documentación del tipo dict describe la sustitución del valor cuando una clave vuelve a aparecer. Una construcción correcta desde el punto de vista del lenguaje puede ser una decisión incorrecta para tus datos.

Podría ser justo el comportamiento que buscas si la entrada fuese una secuencia de actualizaciones ordenadas y la regla fuese conservar la última. En ese caso habría que comprobar que ese orden representa realmente la actualidad del dato. Si, en cambio, dos filas de una entrega pretenden identificar incidencias distintas, quedarse con la última oculta el defecto. «El diccionario se construyó» no es la misma noticia que «el lote no tenía conflictos». Conviene no intercambiarlas en el informe.

Tampoco añadiría un sufijo improvisado a la clave para salvar el recorrido. Estaríamos cambiando la identidad que utiliza el resto de la herramienta. Para nuestro ejercicio elegiremos otra política: una clave repetida rechaza la construcción del índice. Esa regla necesita mantenerse visible, aunque cueste unas líneas más que la versión que acaba de borrar educadamente una de las entradas.

Mantener la validación explícita y la transformación breve

Añade ahora este tercer bloque al mismo archivo. Primero comprueba la forma mínima de las filas, los dos estados del ejercicio y la unicidad del identificador. Solo después construye el índice:

				
					def indexar_estados(filas):
    if not isinstance(filas, list):
        raise TypeError("se requiere una lista reutilizable")
    vistos = set()
    for fila in filas:
        if not isinstance(fila, dict):
            raise ValueError("cada fila debe ser un diccionario")
        identificador = fila.get("id")
        if not isinstance(identificador, str) or not identificador.strip():
            raise ValueError("id ausente o vacio")
        if fila.get("estado") not in ("abierta", "cerrada"):
            raise ValueError("estado no admitido")
        if identificador in vistos:
            raise ValueError("id repetido: " + identificador)
        vistos.add(identificador)
    return {fila["id"]: fila["estado"] for fila in filas}


print("Indice validado:", indexar_estados(incidencias))
try:
    indexar_estados(repetidas)
except ValueError as error:
    print("Lote rechazado:", error)
				
			

El primer print muestra los tres estados originales, con I-01 abierta. La llamada sobre repetidas termina en el mensaje «Lote rechazado: id repetido: I-01». No devuelve un índice parcial. El bucle de validación sigue siendo explícito porque hay varias razones distintas para rechazar y queremos que se entiendan. La comprensión final queda reservada a una transformación sencilla de datos que ya han pasado esas comprobaciones.

La función exige una lista reutilizable porque la recorre dos veces. Si aceptáramos sin más un generador consumible, el segundo recorrido podría quedarse sin elementos. Para una herramienta mayor podríamos diseñar otra interfaz o construir el índice durante la validación; aquí preferimos declarar ese requisito y mostrar claramente las dos fases. No se trata de encontrar la única forma válida de escribirlo, sino de hacer coincidir la promesa de la función con lo que recibe y devuelve.

Los identificadores se comparan exactamente. Comprobamos que no sean vacíos ni solo espacios, pero no recortamos ni cambiamos mayúsculas al construir las claves. Si tu sistema considera equivalentes dos representaciones, esa normalización debe formar parte del contrato y probarse antes de decidir unicidad. El ejercicio tampoco demuestra que las claves pertenezcan a un servidor: solo comprueba las reglas locales que acabamos de declarar.

Para ejecutar los tres bloques juntos, conserva su orden en el mismo archivo y utiliza:

				
					python comprensiones.py
				
			

Así puedes comparar la selección, observar la sustitución silenciosa y verificar el rechazo explícito con los mismos datos. La segunda salida es una demostración del riesgo; la tercera muestra la política elegida. No hace falta esconder la versión problemática: entender por qué produce un resultado aparentemente razonable ayuda a reconocer el mismo fallo cuando aparece dentro de una aplicación con un aspecto mucho más profesional.

La forma breve debe dejar menos trabajo a quien viene detrás

Una comprensión merece quedarse cuando su propósito se reconoce de un vistazo y el resultado tiene una forma clara. Si aparecen varios recorridos anidados, condiciones extensas, tratamiento de excepciones o llamadas que modifican un modelo, revisaría la elección. Una función con nombre puede extraer una regla; un bucle permite explicar etapas y registrar incidencias. Tener sintaxis suficiente para escribirlo no obliga a convertirlo en una sola expresión.

También miraría el tamaño del resultado. Una comprensión de listas materializa la lista; no obtiene por sí misma el comportamiento de un generador. Si solo necesitas recorrer una gran secuencia una vez, puede convenir estudiar otra herramienta. Eso no significa sustituir automáticamente listas por generadores: hemos visto precisamente una función que necesita repetir el recorrido. El criterio útil es qué trabajo y qué memoria exige la operación completa, no qué símbolo parece más avanzado.

No utilizaría una comprensión para ejecutar escrituras y tirar después la lista que produce. Si la intención es actualizar parámetros, enviar solicitudes o guardar archivos, haría visibles ese recorrido y sus efectos. Quien revise la herramienta necesita localizar dónde cambia algo, qué ocurre si falla a mitad y cómo se confirma el resultado. Disfrazar una secuencia de acciones como una colección de valores no la vuelve más elegante; solo retrasa la conversación incómoda.

La selección breve conserva el mismo resultado que el bucle. El índice breve solo merece quedarse después de resolver los duplicados. Ahí termina mi entusiasmo por reducir líneas: si una expresión obliga al siguiente compañero a reconstruir la política de datos, no hemos simplificado el programa; hemos externalizado su lectura. Conserva la forma más corta que permita ver la decisión, no la más corta que acepte el intérprete.

Artículos relacionados

Respuestas

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *