Con el Tema 14 el KR-1 ya mide y actúa. Lo que no sabe todavía es recordar. Este tema le da memoria: estructuras para guardar muchos datos, un archivo donde sobrevivan al apagón y unas gráficas que conviertan ocho mil números en una conclusión. Y cierra el bloque con lo que el currículo llama proceso de desarrollo: edición, interpretación, ejecución, pruebas y depuración, que es la destreza que más distingue a quien programa bien.
medida1, medida2, medida3, medida4. Funciona. Ahora tienes ocho mil. Esa imposibilidad es la razón de que existan las estructuras de datos, y también la razón de que exista el bucle for del tema anterior: las dos cosas van juntas.- crear, recorrer, buscar y modificar listas, y usar sus operaciones habituales;
- explicar qué aporta una tupla frente a una lista y cuándo conviene cada una;
- usar diccionarios para representar los datos de un proyecto con claves con nombre;
- tratar una cadena como secuencia: acceder, trocear y aplicar sus métodos;
- elegir la estructura de datos adecuada a un problema técnico y justificarlo;
- registrar una serie de medidas en un archivo CSV y volver a leerla;
- representar gráficamente una serie y extraer de ella una conclusión;
- describir el proceso de desarrollo completo y la diferencia entre compilar e interpretar;
- clasificar un error, leer su traza y depurar paso a paso con puntos de interrupción.
1. Listas
Una lista es una colección ordenada de elementos, guardada en una sola variable. Es la estructura de datos que más se usa y, con diferencia, la primera que hay que dominar.
medidas = [420, 515, 380, 610]
print(medidas[0]) # 420 el primero es el índice 0
print(medidas[3]) # 610
print(medidas[-1]) # 610 el último, contando desde el final
print(medidas[-2]) # 380
print(len(medidas)) # 4 número de elementos
medidas[4] produce IndexError. Es el error de índice más frecuente y tiene el mismo origen que el range(5) del Tema 14: contar desde cero. El índice negativo -1 es el atajo para el último elemento y evita la mitad de esos errores.| Operación | Qué hace | Ejemplo |
|---|---|---|
lista.append(x) | Añade x al final. | medidas.append(590) |
lista[i] = x | Sustituye el elemento i. | medidas[0] = 425 |
lista.pop() | Saca y devuelve el último. | ultima = medidas.pop() |
x in lista | Devuelve True si x está. | if 4095 in medidas: |
lista.index(x) | Posición de la primera aparición. | medidas.index(610) → 3 |
len(), sum(), min(), max() | Número, suma y extremos. | sum(medidas) / len(medidas) |
sorted(lista) | Devuelve una copia ordenada. | ordenadas = sorted(medidas) |
lista[a:b] | Trozo desde a hasta b sin incluirlo. | medidas[1:3] → [515, 380] |
# La media, ahora en una línea
medidas = [420, 515, 380, 610]
media = sum(medidas) / len(medidas)
print(f"Media: {media:.1f}") # Media: 481.3
# Y la mediana, que resiste mejor un valor disparatado
ordenadas = sorted(medidas)
mitad = len(ordenadas) // 2
if len(ordenadas) % 2 == 1:
mediana = ordenadas[mitad]
else:
mediana = (ordenadas[mitad - 1] + ordenadas[mitad]) / 2
print(f"Mediana: {mediana:.1f}") # Mediana: 467.5
tabla[fila][columna]. Sirve para guardar varias series a la vez —la humedad de tres bancales—, y es la estructura que aparecerá al leer un archivo CSV en la sección 6.2. Tuplas
Una tupla es como una lista pero inmutable: una vez creada, no se puede modificar. Se escribe con paréntesis en lugar de corchetes.
calibracion = (3000, 1200) # (seco, saturado)
seco, saturado = calibracion # desempaquetado en dos variables
print(seco, saturado) # 3000 1200
# calibracion[0] = 2900 # TypeError: no se puede modificar
def leer_sensor():
"""Devuelve varias cosas a la vez: en realidad, una tupla."""
return 2847, 22.5, True # cuentas, temperatura, valido
cuentas, temperatura, valido = leer_sensor()
3. Diccionarios
Un diccionario guarda pares de clave y valor. En lugar de acceder por posición, se accede por un nombre, y eso cambia por completo la legibilidad de un programa de proyecto.
lectura = {
"instante": "2027-07-14 08:15",
"humedad_pct": 8.5,
"temperatura_c": 22.5,
"valvula": False,
"bancal": 2,
}
print(lectura["humedad_pct"]) # 8.5
lectura["valvula"] = True # modificar
lectura["duracion_s"] = 30 # añadir una clave nueva
print(lectura.keys()) # las claves
print(lectura.get("caudal", 0)) # 0 si la clave no existe: no falla
(“2027-07-14 08:15”, 8.5, 22.5, False, 2). Como diccionario: cada dato con su nombre. Con la tupla, seis meses después nadie sabrá si el 2 final es el bancal o el número de riegos, y bastará con intercambiar dos valores para que el programa siga funcionando y mienta. Con el diccionario, lectura["bancal"] se explica solo. En un proyecto, los datos se guardan con su nombre.# Un diccionario también sirve como configuración del sistema
CONFIG = {
"umbral_riego_pct": 30,
"duracion_riego_s": 30,
"hora_inicio": 7,
"hora_fin": 10,
"periodo_medida_s": 900,
}
if humedad < CONFIG["umbral_riego_pct"]:
regar(CONFIG["duracion_riego_s"])
4. Cadenas como secuencias
Una cadena de texto es también una secuencia, y admite los índices y los troceados de una lista.
linea = "2027-07-14;08:15;2847;22.5"
print(linea[0]) # 2
print(linea[0:4]) # 2027 los cuatro primeros caracteres
print(len(linea)) # 26
print(linea.split(";")) # ['2027-07-14', '08:15', '2847', '22.5']
campos = linea.split(";")
fecha = campos[0]
cuentas = int(campos[2])
temperatura = float(campos[3])
| Método | Qué hace | Ejemplo |
|---|---|---|
split(sep) | Parte la cadena en una lista. | Leer una línea de un CSV. |
strip() | Quita espacios y saltos de línea de los extremos. | Limpiar lo que llega de un archivo. |
replace(a, b) | Sustituye texto. | "2,5".replace(",", ".") antes de convertir. |
join(lista) | Une una lista de cadenas con un separador. | ";".join(campos) para escribir un CSV. |
upper(), lower() | Cambia a mayúsculas o minúsculas. | Comparar sin depender de las mayúsculas. |
startswith(x) | Comprueba si empieza por algo. | Saltar la línea de cabecera de un archivo. |
strip() y replace() resuelven los dos problemas de todo dato que viene de fuera: el salto de línea invisible al final —que hace fallar a int() sin que se vea por qué— y la coma decimal española, que Python no acepta. El Tema 13 avisaba de que todo dato externo hay que convertirlo; estos dos métodos son la limpieza previa a esa conversión.5. Elegir la estructura de datos
| Necesito… | Estructura | Ejemplo en el KR-1 |
|---|---|---|
| Muchos valores del mismo tipo, en orden, que crecen. | Lista | Las lecturas del día para promediar. |
| Un conjunto fijo de valores que no debe cambiar. | Tupla | Los dos puntos de calibración. |
| Devolver varias cosas de una función. | Tupla | return cuentas, temperatura, valido |
| Datos heterogéneos, cada uno con su nombre. | Diccionario | Una lectura completa, o la configuración del sistema. |
| Texto que hay que partir o limpiar. | Cadena y sus métodos | Una línea leída del archivo de registro. |
| Una tabla de filas y columnas. | Lista de listas, o lista de diccionarios | El histórico completo del verano. |
6. Series de medidas y ficheros CSV
Una lista vive en la memoria y desaparece al apagar. Para que las ocho mil lecturas del verano sobrevivan hay que escribirlas en un archivo, y el formato es el CSV: texto plano, una fila por línea y los campos separados por un carácter. Lo abre cualquier hoja de cálculo y lo lee cualquier lenguaje.
# Escribir el registro: en la placa y en el ordenador funciona igual
def registrar(instante, cuentas, humedad_pct, rego):
"""Añade una línea al archivo de registro."""
with open("registro.csv", "a") as f:
f.write(f"{instante};{cuentas};{humedad_pct:.1f};{int(rego)}\n")
registrar("2027-07-14 08:15", 2847, 8.5, True)
with open(...) y el modo de aperturaLa forma with garantiza que el archivo se cierra aunque el programa falle a mitad, y eso evita perder datos. Los modos importan: "a" añade al final —lo que quiere un registro—, "w" borra el contenido anterior y empieza de cero, y "r" solo lee. Confundir "a" con "w" es el error que borra el registro de todo el verano en una ejecución.# Leer el registro y convertirlo en una lista de diccionarios
def cargar_registro(ruta="registro.csv"):
lecturas = []
with open(ruta, "r") as f:
for linea in f:
linea = linea.strip()
if not linea or linea.startswith("#"):
continue # salta vacías y comentarios
campos = linea.split(";")
if len(campos) != 4:
continue # línea corrupta: la descarta
lecturas.append({
"instante": campos[0],
"cuentas": int(campos[1]),
"humedad_pct": float(campos[2]),
"rego": campos[3] == "1",
})
return lecturas
datos = cargar_registro()
print(f"{len(datos)} lecturas cargadas")
# Y ahora la conclusión que pedía la pregunta guía
riegos = [d for d in datos if d["rego"]]
CAUDAL_L_H = 4.0 # litros por hora del gotero, del catálogo
DURACION_H = 30 / 3600 # 30 s expresados en horas
agua_usada = len(riegos) * CAUDAL_L_H * DURACION_H
print(f"Riegos: {len(riegos)}")
print(f"Agua consumida: {agua_usada:.1f} L")
7. Representar los datos
Ocho mil números no son una conclusión. Una gráfica sí, y en Jupyter Notebook se obtiene en cuatro líneas con la biblioteca matplotlib.
import matplotlib.pyplot as plt
humedades = [d["humedad_pct"] for d in datos]
UMBRAL_PCT = CONFIG["umbral_riego_pct"]
plt.plot(humedades)
plt.xlabel("Número de lectura")
plt.ylabel("Humedad (%)")
plt.title("Humedad del bancal 2, julio de 2027")
plt.axhline(UMBRAL_PCT, linestyle="--", label="umbral de riego")
plt.legend()
plt.grid(True)
plt.show()
[d["humedad_pct"] for d in datos] construye una lista nueva recorriendo otra. Es una forma abreviada del bucle for con append del Tema 14, y se usa constantemente al tratar datos porque se lee casi como una frase: «la humedad de cada d en datos». No es imprescindible, y una vez vista resulta difícil volver atrás.8. El proceso de desarrollo
El currículo enumera las fases: edición, compilación o interpretación, ejecución, pruebas y depuración. Y añade una que no está en la lista y ocurre siempre: volver a empezar.
Escribir el código fuente en un editor. En Colab, las celdas del notebook; en Thonny, un archivo .py.
En Python, el intérprete lee y ejecuta. En C o C++, un compilador traduce todo antes y produce un ejecutable.
El programa corre con unos datos concretos. En la placa, además, hay que grabarlo antes.
Comprobar con los casos previstos —y con los que no—. Los assert del Tema 13 son pruebas que se ejecutan solas.
Localizar y corregir la causa de un comportamiento incorrecto. Es la sección 10.
Cada corrección es una iteración del Tema 1, y cada cambio relevante va al registro de cambios del Tema 4.
Compilar
Se traduce todo el programa a código máquina antes de ejecutar. Los errores de sintaxis aparecen de golpe al compilar; el ejecutable resultante es rapidísimo y no necesita el compilador para funcionar.
Interpretar
Se lee y ejecuta línea a línea. Los errores aparecen cuando la ejecución llega a ellos; se puede probar al instante, y hace falta el intérprete en la máquina que ejecute.
else de tu programa tiene una errata y nunca se cumple esa condición durante las pruebas, el programa parecerá correcto hasta el día que se cumpla —en agosto, en el huerto, sin nadie delante—. De ahí la insistencia en los casos de prueba: en un lenguaje interpretado, lo que no se ejecuta no se comprueba.9. Módulos y bibliotecas
Un módulo es un archivo con funciones reutilizables; una biblioteca, un conjunto de módulos. Python trae muchos incluidos —la biblioteca estándar— y se importan con import.
| Módulo | Para qué | Dónde |
|---|---|---|
math | Funciones matemáticas, π, raíces, trigonometría. | Cálculos de los bloques C, D y G. |
time | Esperas y marcas de tiempo. | Cadencia de medida y duración del riego. |
statistics | Media, mediana y desviación típica. | Tratamiento de series de medidas. |
csv | Lectura y escritura robusta de CSV. | Alternativa a hacerlo a mano, en el ordenador. |
matplotlib | Gráficas. | Memoria del proyecto. |
machine | Pines, ADC y PWM de la placa. | Solo en MicroPython, temas 14 a 19. |
network, umqtt | Wi-Fi y mensajería. | Solo en MicroPython, Tema 16. |
import statistics
humedades = [d["humedad_pct"] for d in datos]
print(f"Media: {statistics.mean(humedades):.1f} %")
print(f"Mediana: {statistics.median(humedades):.1f} %")
print(f"Desv. típica: {statistics.stdev(humedades):.1f} %")
kr1.py, cualquier programa puede usarlas con import kr1. Eso es la modularización que pide el currículo, y es lo que permite que el programa de la placa y el análisis de datos del ordenador compartan exactamente la misma función de calibración, sin copiarla dos veces. Y cuando se recalibre el sensor, se toca un archivo.10. Errores y depuración
| Tipo | Cuándo aparece | Cómo se localiza |
|---|---|---|
| De sintaxis | Antes de ejecutar nada: el intérprete no entiende la escritura. | El mensaje señala la línea. Mirar también la anterior. |
| De ejecución (excepción) | Durante la ejecución: IndexError, ZeroDivisionError, ValueError, KeyError… | La traza dice el tipo, la línea y el camino recorrido. |
| Lógico | Nunca avisa: el programa termina y el resultado está mal. | Casos de prueba, traza y depurador. Es el 90 % del tiempo de depuración. |
ZeroDivisionError en la línea 40 suele tener su origen en la línea 12, donde una lista se quedó vacía. La traza dice dónde se rompió; encontrar por qué es el trabajo.Las tres herramientas de depuración, de peor a mejor
1. print()
Imprimir el valor de las variables en puntos clave. Es tosco, funciona siempre y es lo único disponible dentro de la placa. Regla: imprime el nombre junto al valor.
2. assert
Comprobar que una condición se cumple justo donde debe. Detiene el programa en el punto exacto en que el estado deja de ser correcto, en lugar de quinientas líneas después.
3. El depurador
Ejecutar paso a paso viendo el valor de todas las variables. Es la traza del Tema 14, generada automáticamente. En Thonny está a un clic.
Depurar paso a paso con Thonny
Pulsar sobre el número de línea donde quieres que el programa se detenga. Se elige la primera línea donde sospechas que algo va mal.
El programa arranca y se detiene en ese punto, con el estado completo a la vista.
Thonny muestra todas las variables y su valor. Aquí se responde a «¿en qué paso deja de ser lo que esperaba?».
Paso a paso: ejecutar la línea siguiente, o entrar dentro de la función que se llama para ver qué hace por dentro.
Y añadir un caso de prueba que reproduzca el fallo, para que si vuelve, avise.
# La batería de pruebas de la función del Tema 14, completa
assert media_valida([420, 515]) == 467.5 # normal
assert media_valida([]) is None # lista vacía
assert media_valida([420]) == 420 # un solo elemento
assert media_valida([4095, 4095]) is None # todas no válidas
assert media_valida([0, 4090]) == 2045 # los límites, incluidos
print("Las cinco pruebas pasan")
Comprueba lo que has aprendido
Este tema se demuestra con un registro real, una gráfica con conclusión y una sesión de depuración documentada.
| Aspecto | Qué debes demostrar | Evidencias posibles |
|---|---|---|
| Estructuras de datos | Que eliges lista, tupla o diccionario con criterio y lo justificas. | Histórico como lista de diccionarios, calibración como tupla y una frase que explique cada elección. |
| Registro | Que las medidas sobreviven al apagado y se pueden volver a leer. | Archivo CSV real con cientos de líneas y la función de carga que descarta líneas corruptas. |
| Análisis y gráfica | Que conviertes la serie en una conclusión. | Gráfica con título, ejes rotulados con unidad y línea de umbral; cálculo del agua consumida. |
| Modularización | Que tus funciones están en un módulo propio reutilizado por dos programas. | Archivo kr1.py importado tanto por el programa de la placa como por el análisis. |
| Depuración | Que aplicas el método y no cambias cosas al azar. | Un fallo documentado con sus cuatro pasos, captura del depurador con el punto de interrupción y la prueba añadida. |
| Pruebas | Que tu batería cubre los casos límite. | Cinco assert con caso normal, vacío, único, límites y valores imposibles. |
Cierre del aprendizaje del lenguaje: con el Tema 16 el KR-1 saldrá a la red, y a partir del Bloque F Python ya no se explica, se usa.
Glosario del tema
- Biblioteca
- Conjunto de módulos con funciones reutilizables.
- Clave
- Nombre con el que se accede a un valor dentro de un diccionario.
- Comprensión de lista
- Expresión que construye una lista nueva recorriendo otra en una sola línea.
- CSV
- Formato de archivo de texto con una fila por línea y campos separados por un carácter.
- Depuración
- Proceso de localizar y corregir la causa de un comportamiento incorrecto.
- Depurador
- Herramienta que ejecuta un programa paso a paso mostrando el valor de las variables.
- Diccionario
- Estructura que guarda pares de clave y valor, con acceso por nombre.
- Excepción
- Error que se produce durante la ejecución e interrumpe el programa.
- Índice
- Posición de un elemento en una secuencia; el primero es el cero.
- Inmutable
- Que no se puede modificar después de creado, como una tupla o una cadena.
- JSON
- Formato de intercambio de datos en texto, equivalente a un diccionario; se usa en el Tema 16.
- Lista
- Colección ordenada y modificable de elementos.
- Mediana
- Valor central de una serie ordenada; más robusta que la media frente a valores disparatados.
- Modularización
- Organización del código en módulos reutilizables e independientes.
- Módulo
- Archivo con funciones que puede importarse desde otro programa.
- Punto de interrupción
- Línea en la que se pide al depurador que detenga la ejecución.
- Traza de excepción
- Informe que indica el tipo de error, la línea y la cadena de llamadas que llevó hasta él.
- Troceado
- Extracción de una parte de una secuencia indicando el índice inicial y el final.
- Tupla
- Colección ordenada e inmutable de elementos.
Resumen esencial
- Una lista guarda muchos valores en orden; el primer índice es el 0 y el último,
len − 1. - El índice negativo
-1es el último elemento y evita la mitad de los errores de índice. - En series con ruido o fallos, la mediana es más robusta que la media: elegir una u otra es decidir cómo se comporta el sistema cuando un sensor falla.
- Una tupla es inmutable: sirve para datos que no deben cambiar y para devolver varios valores de una función.
- Un diccionario guarda cada dato con su nombre, y eso es lo que hace legible el histórico de un proyecto seis meses después.
- El formato JSON del Tema 16 es, para lo que nos interesa, un diccionario escrito como texto.
- Una cadena es una secuencia:
split,stripyreplaceson la limpieza previa a toda conversión de un dato externo. - Muchos datos iguales, lista; datos distintos con nombre, diccionario; datos que no cambian, tupla; un histórico, lista de diccionarios.
with open(...)cierra el archivo aunque el programa falle; y confundir el modo"a"con"w"borra el registro de todo el verano.- Un archivo real siempre tiene líneas malas: un lector robusto descarta las vacías y las que no tienen los campos esperados.
- Una gráfica técnica lleva título, ejes rotulados con unidad, escala razonable y las referencias que permiten interpretarla.
- El proceso de desarrollo es edición, interpretación o compilación, ejecución, pruebas y depuración, y siempre otra vuelta.
- En un lenguaje interpretado, lo que no se ejecuta no se comprueba: una errata en una rama que nunca se cumple espera hasta agosto.
- Guardar tus funciones en un módulo propio permite que la placa y el análisis compartan la misma calibración.
- Hay tres tipos de error: sintaxis, ejecución y lógico; el lógico no avisa y consume la mayor parte del tiempo de depuración.
- La traza se lee de abajo arriba, y la línea que falla no siempre es la línea culpable.
- Depurar es reproducir, acotar, formular una hipótesis y corregir añadiendo la prueba que impida que vuelva.
- La batería de pruebas cubre siempre el caso normal, el vacío, el de un elemento, los límites, los valores imposibles y el que ya falló una vez.
Fuentes y recursos fiables
- [1] Tutorial oficial de Python — Estructuras de datos (en español).
- [2] Tutorial oficial de Python — Entrada y salida, y lectura de archivos (en español).
- [3] Documentación del módulo
statistics(en español). - [4] Matplotlib — Guía de inicio rápido (en inglés).
- [5] Thonny. Depurador paso a paso con puntos de interrupción e inspección de variables.
- [6] Tutorial oficial de Python — Errores y excepciones (en español).
- [7] Decreto 64/2022, de 20 de julio (BOCM). Currículo de Tecnología e Ingeniería I, bloque E.