# Python · Programa completo

Desde cero hasta funciones, objetos, pruebas, SQL, APIs, asincronía y ciencia de datos con proyectos reproducibles. Python real y revisión de cada práctica.

40 módulos · 200 prácticas

## 1. Un lenguaje, un lugar para experimentar

Python es el lenguaje; el editor organiza sus instrucciones. Aquí ejecuta Python real en su navegador, sin instalar nada. Cada revisión comienza con variables nuevas para comprobar que su script es reproducible.

El editor contiene su script. Ejecutar envía todo el código; Ctrl o Cmd + Enter ejecuta el script completo.

El panel Consola muestra la salida de su código. La última expresión muestra su valor; print() hace visible un resultado dentro de un script.

Variables muestra lo que creó. Las tablas, figuras y archivos tienen sus propios paneles.

Cada revisión empieza con variables nuevas; incluya todos los pasos en su script. El avance y los borradores se guardan en este navegador; descargue los archivos para conservarlos.

- Su primera instrucción: Muestre exactamente Hola, laboratorio con print().
- Una respuesta inmediata: Calcule 12 + 8 como última expresión.
- El panel Variables: Cree muestras con el entero 6.
- Un script de dos pasos: Cree placas = 3 y después imprima placas * 96.
- Preguntarle a Python: Consulte help(len).

Referencia: https://docs.python.org/3.13/tutorial/

## 2. Cuentas de laboratorio que se pueden repetir

Una dilución, una potencia o un logaritmo son buenos primeros programas. Python respeta la precedencia matemática; los paréntesis expresan el orden que necesita.

** eleva a una potencia. ^ opera entre bits; no es una potencia.

* y / se evalúan antes que + y −. // hace división entera; % devuelve el residuo.

Importar math da acceso a raíces, logaritmos y otras funciones.

Los decimales usan punto flotante: 0.1 + 0.2 puede mostrar 0.30000000000000004. Redondee al presentar y compare con tolerancia al calcular.

- C1 · V1 = C2 · V2: Calcule el volumen de stock en µL como última expresión.
- Amplificación ideal: Calcule 2**30.
- Los paréntesis importan: Con 8 positivos y 2 negativos, calcule el porcentaje usando paréntesis.
- Logaritmos con math: Importe math y calcule math.log10(100000).
- Presentar un resultado: Redondee 10 / 3 a dos decimales.

Referencia: https://docs.python.org/3.13/tutorial/

## 3. Dar nombre a los datos y a sus resultados

Una variable relaciona un nombre con un objeto. Nombres como ct_il6 permiten leer la intención de una cuenta y reutilizarla con nuevas mediciones.

= asigna; == compara. Python distingue Ct de ct.

Use nombres descriptivos con guiones bajos. Evite llamar list, sum o str a una variable.

Cambiar una entrada no recalcula los resultados anteriores: vuelva a ejecutar la operación.

Defina las entradas antes de operar. Un script que calcula en orden puede ejecutarse desde una sesión vacía y producir el mismo resultado.

- Nombrar mediciones: Asigne 28.3 a ct_il6 y 18.3 a ct_gapdh.
- Calcular con nombres: Guarde ct_il6 - ct_gapdh en dct.
- Recalcular explícitamente: Cambie ct_il6 a 25.3 y recalcule dct con las variables.
- Cambio relativo: Cree ddct = -3 y fold = 2**(-ddct).
- Actualizar un contador: Empiece con procesadas = 6 y añada 3 mediante +=.

Referencia: https://docs.python.org/3.13/tutorial/

## 4. Parecido no significa equivalente

El número 28.3 y el texto '28.3' se ven parecidos, pero solo uno permite calcular. Reconozca y convierta los tipos antes de procesar sus datos.

int representa enteros; float, decimales; str, texto; bool, True o False.

None representa ausencia: no es cero ni el texto 'None'.

Los métodos .strip() y .upper() devuelven una cadena nueva.

Una f-string inserta expresiones entre llaves: f'Ct: {ct:.2f}' presenta dos decimales sin cambiar el número original.

- Texto que parece número: Convierta "28.3" a float en ct_num.
- Limpiar un identificador: Transforme "  il6  " en "IL6" dentro de gen, usando ambos métodos.
- Un mensaje con precisión: Con ct = 28.3, cree mensaje igual a Ct: 28.30 mediante una f-string.
- Comparar produce un tipo: Cree detectado a partir de 28.3 < 35.
- Ausente no es cero: Cree lectura = None y falta = lectura is None.

Referencia: https://docs.python.org/3.13/tutorial/

## 5. Colecciones, índices y cortes

Una lista guarda varios objetos en un orden. Sirve para reunir mediciones, elegir elementos y construir resultados paso a paso.

El primer índice es 0. El índice −1 recupera el último elemento.

[inicio:fin] incluye inicio y excluye fin; omitir un límite usa el extremo.

Multiplicar una lista por 2 la repite. Para multiplicar cada valor se usa NumPy.

b = a comparte la misma lista; b = a.copy() crea una copia independiente para una lista de valores simples.

- Una serie de Ct: Cree replicas = [18.2, 18.4, 18.3] y deje len(replicas) al final.
- Primero y último: Con la lista ct, guarde el primero en primero y el último en ultimo usando índices.
- El final queda fuera: Guarde los primeros tres valores de ct en primeros mediante un corte.
- Añadir una medición: Cree valores = [18.2, 18.4] y añada 18.3 con append.
- Compartir no es copiar: Cree original = [1, 2, 3], cópiela en copia y cambie copia[0] a 99 sin alterar original.

Referencia: https://docs.python.org/3.13/tutorial/

## 6. Identificar cada dato por su nombre

Un diccionario guarda parejas de clave y valor: es una forma natural de representar una muestra. Sus claves permiten acceder por significado.

Las llaves crean el diccionario; muestra['ct'] accede a una clave.

Una clave ausente produce KeyError. .get() permite elegir una alternativa.

Una lista de diccionarios representa registros; un set reúne elementos sin duplicados.

Lista para una secuencia, diccionario para campos con nombre y set para unicidad o pertenencia. Elegir bien simplifica el siguiente paso.

- Una muestra con nombres: Cree muestra con id C1, gen IL6 y ct 28.3.
- Acceder por significado: Guarde el campo ct de muestra en lectura.
- Un campo que puede faltar: Busque lote en muestra y guarde sin dato en lote si falta, usando get.
- Añadir metadatos: Cree muestra con id C1 y añada grupo con valor control.
- Elementos únicos: Cree genes como set de ["IL6", "GAPDH", "IL6"] y deje su longitud al final.

Referencia: https://docs.python.org/3.13/tutorial/

## 7. Hacer que el programa responda a sus datos

Una condición produce True o False. if, elif y else eligen qué instrucciones ejecutar. Los bloques dependen de la sangría.

Cada cabecera termina en dos puntos. Use cuatro espacios dentro del bloque.

and, or y not combinan booleanos. Para arrays o columnas se usa & o | con paréntesis.

< 35 excluye 35; <= 35 lo incluye. Pruebe siempre los límites.

El umbral Ct del curso sirve para aprender condiciones. La validez experimental depende del ensayo, su diseño y sus controles.

- Una decisión sencilla: Con ct = 28.3, use if/else: si ct < 35, estado es detectado; en otro caso, revisar.
- Probar el límite: Con ct = 35, use if/else con ct < 35 y guarde revisar en estado.
- Dos condiciones a la vez: Con ct = 28.3, cree en_rango comprobando ct > 15 y ct < 35 mediante and.
- Tres posibilidades: Con lectura = None, use if/elif/else: None → sin dato; < 35 → detectado; otro caso → revisar. Guarde en estado.
- Consultar pertenencia: Cree es_referencia comprobando si "GAPDH" está en ["GAPDH", "ACTB"].

Referencia: https://docs.python.org/3.13/tutorial/

## 8. Repetir una idea sin repetir el código

Un bucle recorre una colección, acumula resultados o filtra lecturas. Una comprensión permite expresar transformaciones pequeñas de forma compacta.

for elemento in coleccion: ejecuta un bloque para cada elemento.

range(5) genera de 0 a 4. zip() empareja secuencias.

Una comprensión puede filtrar: [x for x in datos if x > 0].

Un while necesita una condición que cambie. Detener reinicia el proceso de Python si su código no termina; conserva el editor y su avance.

- Transformar cada lectura: Use for y append para multiplicar [0.1, 0.2, 0.3] por 50 y crear concentraciones.
- Filtrar con una comprensión: Cree validos con los ct menores que 35 mediante una comprensión.
- Un límite exclusivo: Use range y list para crear replicas = [1, 2, 3, 4, 5].
- Emparejar secuencias: Cree pares como lista de zip de ["C1", "C2"] y [28.2, 28.4].
- Un bucle que termina: Empiece con ciclos = 0 y use while para incrementarlo hasta 3.

Referencia: https://docs.python.org/3.13/tutorial/

## 9. Convertir un cálculo en una herramienta

Una función recibe argumentos, ejecuta instrucciones y devuelve un resultado. Escriba una vez la regla de una dilución y reutilícela.

def nombre(argumentos): define la función. Su cuerpo lleva sangría.

return entrega un resultado; print() lo muestra, pero no lo devuelve.

Los valores predeterminados y los argumentos por nombre hacen explícita su intención.

Una función que devuelve un número fijo puede acertar un ejemplo y fallar con otras entradas. Aquí revisamos las funciones con varios casos.

- Una función que devuelve: Defina dilucion(c1, c2, v2) que devuelva c2 * v2 / c1.
- Su propia transformación: Defina cambio(ddct) que devuelva 2**(-ddct).
- Un parámetro predeterminado: Defina ajustar(ct, referencia=18.3) que devuelva ct - referencia.
- Aplicar una función: Defina cambio y cree cambios aplicándola a [-3, 0, 2] con una comprensión.
- Explicar una función: Defina delta(ct, ref), que devuelva ct - ref, con una docstring que incluya referencia.

Referencia: https://docs.python.org/3.13/tutorial/

## 10. Leer un error es parte de programar

Un error dice qué no pudo hacer Python y en qué línea ocurrió. Aprenda a corregir su causa y manejar los casos que espera encontrar.

NameError indica un nombre ausente; TypeError, un tipo incompatible; ValueError, un valor no admitido.

try/except captura excepciones concretas. Evite silenciar todo con except: pass.

assert comprueba y documenta una expectativa de su código.

Las líneas anteriores a un error sí pudieron modificar objetos. Corrija y ejecute el script completo para recuperar un estado conocido.

- Reparar un nombre: Corrija el nombre y guarde ct - 18.3 en dct.
- Reparar un tipo: Con ct_texto = "28.3", convierta el dato y guarde la diferencia con 18.3 en dct.
- Una conversión que puede fallar: Defina leer_ct(texto): devuelva float(texto) o None ante ValueError, usando try/except.
- Rechazar una entrada inválida: Defina volumen(c1, c2, v2): lance ValueError si c1 <= 0; en otro caso devuelva c2 * v2 / c1.
- Dejar una prueba en el script: Defina cambio(ddct) y añada assert cambio(0) == 1.

Referencia: https://docs.python.org/3.13/tutorial/

## 11. De un archivo de texto a datos utilizables

El proyecto incluye archivos CSV con datos simulados. La biblioteca estándar csv permite leerlos sin necesitar pandas.

Las rutas son relativas al proyecto: datos/qpcr.csv. Revise Archivos.

with open(...) cierra el archivo incluso si algo falla. Declare encoding='utf-8'.

csv.DictReader reconoce encabezados, pero conserva los campos como texto.

Los archivos viven en la memoria de Python. Descárguelos: reiniciar restaura los datos de ejemplo y elimina los archivos de la sesión.

- Abrir el archivo de práctica: Lea datos/qpcr.csv con encoding utf-8 y guárdelo en texto.
- Cada fila con su encabezado: Importe csv y lea datos/qpcr.csv como lista de diccionarios llamada filas.
- Convertir la columna: Lea el CSV con DictReader y cree cts como lista de floats de la columna ct.
- Guardar una nota: Escriba Análisis de práctica y un salto de línea en resultados/notas.txt con with open.
- Exportar una tabla: Cree resultados/resumen.csv con encabezado gen,fold y una fila IL6,8 mediante csv.writer.

Referencia: https://docs.python.org/3.13/tutorial/

## 12. Cálculo sobre colecciones completas

NumPy aporta arrays numéricos. Una operación transforma todos sus elementos y expresa las matemáticas de forma directa.

np.array(...) crea un array después de import numpy as np.

np.std(..., ddof=1) calcula la desviación estándar muestral.

np.nan representa una ausencia; np.nanmean() la omite y np.isnan() la localiza.

En una matriz de filas por columnas, axis=0 resume por columna; axis=1, por fila. Inspeccione .shape antes de elegir.

- Un array numérico: Importe numpy como np y cree a260 como array con [0.1, 0.2, 0.3].
- Una operación, todos los valores: Cree el array a260 y calcule concentraciones = a260 * 50.
- Media y dispersión: Con replicas = np.array([18.2, 18.4, 18.3]), cree media con mean y sd con std y ddof=1.
- Ausencias numéricas: Con x = np.array([22.0, np.nan, 24.0]), calcule media con nanmean y faltantes con isnan y sum.
- Elegir el eje: Cree matriz = np.array([[18, 20], [24, 26]]) y guarde sus medias por fila en medias.

Referencia: https://numpy.org/doc/stable/user/absolute_beginners.html

## 13. Leer, limpiar y resumir una tabla

Un DataFrame relaciona filas y columnas con sus nombres. pandas permite leer, seleccionar y agrupar sin escribir un bucle por operación.

pd.read_csv() reconoce números y ausencias. .head(), .shape y .dtypes ayudan a inspeccionar.

Seleccione con .loc[condicion, columnas] y use .copy() si va a modificar un subconjunto.

groupby() divide por claves; agregue .mean() para obtener un resumen.

Compruebe qué representa una fila y qué valores faltan. Las tres muestras por grupo de este curso representan réplicas biológicas simuladas.

- Leer un DataFrame: Importe pandas como pd, lea datos/qpcr.csv en qpcr y deje qpcr al final.
- Seleccionar por condición: Cree il6 con las filas cuyo gen sea IL6, usando .loc y .copy().
- Resumir por grupo y gen: Cree medias con la media de ct por grupo y gen mediante groupby.
- Contar antes de limpiar: Lea datos/muestras.csv en tabla; cree faltantes como cantidad de ct ausentes y limpia sin esas filas.
- Ordenar registros completos: Cree ordenada con qpcr ordenada por ct de menor a mayor.

Referencia: https://pandas.pydata.org/docs/getting_started/intro_tutorials/

## 14. Una figura que responda una pregunta

Matplotlib crea figuras reales que puede descargar. La interfaz fig, ax separa el lienzo de sus ejes.

fig, ax = plt.subplots() crea figura y ejes. plot() conecta puntos y scatter() muestra pares.

hist() muestra una distribución y bar() representa resúmenes.

Etiquete unidades y guarde con fig.savefig('resultados/figura.png').

El panel Figuras recoge las figuras abiertas al terminar la ejecución. savefig también crea un archivo descargable en el proyecto.

- Una curva de crecimiento: Lea datos/crecimiento.csv en crecimiento y trace hora frente a od600 con ax.plot, marcadores y etiquetas Hora (h) y OD600.
- Pares sin conectar: Lea datos/curva_estandar.csv en curva y grafique log_copias frente a ct con ax.scatter y etiquetas en ambos ejes.
- Una distribución: Cree un histograma de ct de qpcr con 5 intervalos y etiquetas en ambos ejes.
- Mostrar un cambio relativo: Cree barras control y tratado de alturas 1 y 8. Etiquete y como Expresión relativa de IL6.
- Una figura descargable: Trace [0, 1, 2] frente a [1, 2, 4] y guarde resultados/figura.png con dpi=150.

Referencia: https://docs.python.org/3.13/tutorial/

## 15. Una estimación y sus supuestos

SciPy y NumPy ajustan curvas y comparan grupos. El código resuelve la cuenta; usted decide si el diseño y sus supuestos sostienen una interpretación.

En una curva estándar, Ct frente a log10(copias) tiene pendiente negativa.

Eficiencia = (10**(-1/pendiente) - 1) * 100. Una recta no valida todo el ensayo.

Welch compara medias independientes sin imponer igual varianza; una p pequeña no mide la magnitud del efecto.

No trate pozos técnicos como muestras independientes. Estos datos sintéticos y muy regulares permiten verificar cuentas, no obtener conclusiones biológicas.

- Ajustar la curva estándar: Lea datos/curva_estandar.csv en curva y cree ajuste con stats.linregress de log_copias frente a ct.
- Interpretar la pendiente: Con pendiente = -3.3, calcule eficiencia = (10**(-1 / pendiente) - 1) * 100.
- Comparar grupos independientes: Cree prueba con stats.ttest_ind entre [28.2, 28.4, 28.3] y [25.2, 25.4, 25.3], usando equal_var=False.
- Cuánto cambian las medias: Use statistics.mean para calcular diferencia: media de tratado menos media de control, con los mismos grupos.
- Aleatorio reproducible: Cree rng = np.random.default_rng(42) y simulados = rng.normal(18.3, 0.1, 5).

Referencia: https://docs.python.org/3.13/tutorial/

## 16. Un análisis que se puede volver a ejecutar

Integre lectura, transformación, resumen, figura y exportación. El proyecto analiza IL6 con GAPDH como referencia y compara control con tratamiento.

Empareje IL6 y GAPDH por muestra para obtener ΔCt; no reste por posición de fila.

Calibrador = media de ΔCt de controles. ΔΔCt = ΔCt − calibrador. Cambio = 2**(-ΔΔCt).

Exporte resultados y script. Valide las eficiencias compatibles antes de aplicar el método a datos reales.

Los datos de ejemplo tienen ΔCt de 10 en controles y 7 en tratados: ΔΔCt de −3 y cambio relativo de 8 en cada tratado. No representan variación experimental real.

- Una fila por muestra: Cree ancha con pivot: índice ["muestra", "grupo"], columnas gen y valores ct.
- Normalizar por muestra: Lea el CSV, cree ancha y añada su columna dct como IL6 menos GAPDH.
- Elegir el calibrador: Complete el script: calcule calibrador y las columnas ddct y fold de ancha.
- Un resultado revisable: Ejecute el análisis y exporte resultados/ddct.csv con muestra, grupo, GAPDH, IL6, dct, ddct y fold.
- El análisis completo: Complete el análisis, cree resumen de fold por grupo, grafique control y tratado, y guarde resultados/ddct.csv y resultados/il6.png.

Referencia: https://docs.python.org/3.13/tutorial/

## 17. Tuplas, conjuntos y colecciones especializadas

Elija una estructura según las operaciones que necesita.

Una tupla fija posiciones; un conjunto representa pertenencia sin duplicados; un diccionario relaciona claves únicas con valores. La mutabilidad y el hash determinan si un objeto puede usarse como clave.

collections ofrece Counter para frecuencias, defaultdict para agrupación y deque para colas eficientes. Elegir una estructura adecuada simplifica el algoritmo; no convierta todas las relaciones en listas de búsquedas repetidas.

- Desempaquetar registros: Desempaque ("A",2.5,True) en identificador,valor,valido.
- Operar conjuntos: Cree comunes de {"A","B","C"} y {"B","C","D"}.
- Contar frecuencias: Cree frecuencias de ["A","B","A","C","A"] usando Counter.
- Agrupar valores: Agrupe [("A",1),("B",2),("A",3)] en grupos con listas por clave.
- Construir una cola: Cree cola=deque([1,2,3]); retire el primero en atendido y agregue 4 al final.

Referencia: https://docs.python.org/3.13/library/collections.html

## 18. Comprensiones y funciones de orden superior

Transforme colecciones con operaciones pequeñas y explícitas.

Una comprensión reúne expresión, recorrido y filtro. Las comprensiones de diccionario y conjunto usan la misma idea. Evite anidaciones profundas que hagan difícil ver el orden o el coste del recorrido.

Las funciones son objetos y pueden ser argumentos. sorted(key=...) expresa un criterio de ordenación; map, filter y zip producen iteradores. Una lambda sirve para expresiones cortas; una función nombrada explica mejor reglas complejas.

- Filtrar y transformar: Cree cuadrados de los pares de range(8).
- Construir un índice: Cree indice={"A":0,"B":1,"C":2} desde enumerate.
- Ordenar por criterio: Ordene ["ccc","a","bb"] por longitud en ordenadas.
- Combinar secuencias: Cree pares combinando ["A","B"] y [2,4].
- Aceptar una función: Defina aplicar(xs,fn) que devuelva una lista con fn sobre cada x.

Referencia: https://docs.python.org/3.13/tutorial/

## 19. Clases, estado e invariantes

Modele objetos que combinan datos y operaciones coherentes.

Una clase define comportamiento; una instancia conserva su propio estado. __init__ inicializa atributos y los métodos reciben self. Los atributos de clase se comparten: una lista mutable declarada en la clase puede producir errores entre instancias.

Un constructor debe asegurar invariantes. Prefiera nombres claros y métodos pequeños. La orientación a objetos es útil para estado con reglas; una función pura puede ser más sencilla para una transformación independiente.

- Crear una clase: Defina Medicion con __init__(valor) y atributo valor.
- Añadir un método: Defina Contador que empiece en 0 y sumar(n=1) actualice y devuelva valor.
- Separar instancias: Defina Registro con items=[] en __init__ y agregar(x).
- Validar construcción: Defina Positivo(valor) que lance ValueError si valor<=0 y guarde valor en otro caso.
- Representar un objeto: Defina Muestra(id) cuyo repr sea Muestra(A) para id A.

Referencia: https://docs.python.org/3.13/tutorial/classes.html

## 20. Herencia, composición y protocolos

Reutilice comportamiento sin crear dependencias difíciles de cambiar.

La herencia modela una relación de sustitución; super() permite reutilizar la implementación base. La composición entrega una dependencia a otro objeto sin exigir una jerarquía. El polimorfismo permite usar objetos con un protocolo compartido.

Los métodos especiales como __len__ y __iter__ integran una clase con operaciones del lenguaje. Evite heredar solo para reutilizar unas líneas; una función o una dependencia explícita pueden expresar mejor la relación.

- Extender comportamiento: Defina Base.nombre()="base" y Hija(Base).nombre()="hija".
- Usar super: Defina Base con valor; Doble(Base) debe inicializar el doble con super().__init__.
- Componer una estrategia: Defina Procesador(fn) con ejecutar(x) que devuelva fn(x).
- Implementar longitud: Defina Lote(items) que permita len(lote).
- Implementar iteración: Defina Serie(valores) para que list(Serie([2,4])) sea [2,4].

Referencia: https://docs.python.org/3.13/reference/datamodel.html

## 21. Dataclasses, propiedades y modelos

Declare registros con contratos legibles y valores predeterminados seguros.

dataclass genera métodos de inicialización, representación y comparación a partir de campos anotados. field(default_factory=list) crea una colección independiente por instancia. frozen=True impide asignaciones directas a campos.

Una anotación no valida valores durante la ejecución. __post_init__ y propiedades permiten proteger reglas; las propiedades calculadas conservan una sola fuente de verdad cuando un resultado depende de otros campos.

- Declarar un registro: Defina @dataclass Muestra con id:str y valor:float.
- Predeterminados seguros: Defina @dataclass Lote con items:list=field(default_factory=list).
- Convertir a diccionario: Cree @dataclass Punto(x:int,y:int) y registro=asdict(Punto(2,3)).
- Calcular una propiedad: Defina Rectangulo(ancho,alto) con propiedad area.
- Validar tras inicializar: Defina @dataclass Medida(valor:float) que rechace negativos en __post_init__.

Referencia: https://docs.python.org/3.13/library/dataclasses.html

## 22. Iteradores, generadores y procesamiento incremental

Procese secuencias sin materializar todo el conjunto.

Un iterable produce un iterador mediante iter(); next() solicita el siguiente valor hasta StopIteration. Una función con yield conserva su estado entre llamadas y produce resultados bajo demanda.

Un generador suele agotarse tras un recorrido. El procesamiento incremental reduce memoria, pero no elimina el coste del algoritmo. Use itertools para recorridos combinados y evite listas gigantes cuando solo necesita un total.

- Crear un generador: Defina pares(n) que genere los pares de range(n).
- Consumir con next: Cree it=iter([2,4,6]); guarde primero y segundo usando next.
- Generar una suma: Calcule total=sum(x*x for x in range(1000)).
- Limitar un recorrido: Cree primeros con los cinco primeros valores de count(10).
- Procesar por bloques: Defina bloques(xs,n) que genere listas consecutivas de hasta n elementos.

Referencia: https://docs.python.org/3.13/library/itertools.html

## 23. Clausuras, decoradores y caché

Extienda funciones manteniendo su interfaz y su identidad.

Una clausura conserva nombres del ámbito exterior. Un decorador recibe una función y devuelve otra que añade comportamiento. functools.wraps preserva metadatos necesarios para documentación e inspección.

lru_cache evita repetir cálculos de funciones con entradas hashables y resultados estables. La caché también consume memoria y puede producir resultados obsoletos si una función depende de estado externo.

- Crear una clausura: Defina multiplicador(k) que devuelva una función x*k.
- Preservar metadatos: Defina identidad(fn) con @wraps y un wrapper que reenvíe *args,**kwargs.
- Memorizar resultados: Defina @lru_cache(maxsize=32) cuadrado(x) y llámelo dos veces con 4.
- Aplicación parcial: Cree base_dos=partial(pow,2) y resultado=base_dos(5).
- Contar llamadas: Defina contar(fn) cuyo wrapper tenga atributo llamadas, incrementado en cada ejecución.

Referencia: https://docs.python.org/3.13/library/functools.html

## 24. Context managers y gestión de recursos

Libere recursos incluso cuando una operación falle.

with llama al protocolo de contexto para adquirir y liberar recursos. __exit__ recibe la excepción si la hubo; devolver True la suprime. No suprima errores sin una política explícita.

contextlib.contextmanager permite escribir contextos con yield y finally. Los archivos, transacciones y bloqueos tienen ciclos de vida que deben cerrarse; try/finally es la base del patrón.

- Cerrar un archivo: Escriba Hola en saludo.txt con UTF-8 y léalo en texto.
- Cerrar un buffer: Con contextlib.closing y io.StringIO("dato"), lea texto y verifique que buffer quedó cerrado.
- Crear un contexto: Defina recurso() que agregue abrir,cerrar a eventos usando try/finally; dentro del with agregue usar.
- Gestionar una excepción esperada: Con suppress(KeyError), consulte {}["ausente"] y luego cree continuo=True.
- Garantizar limpieza: Cree eventos; dentro de try lance ValueError y capture; añada limpio en finally.

Referencia: https://docs.python.org/3.13/library/contextlib.html

## 25. Módulos, imports y espacios de nombres

Organice código reutilizable y controle qué sucede al importarlo.

Un módulo es un archivo Python. import conserva un espacio de nombres; from importa nombres concretos. El bloque if __name__ == "__main__" permite ejecutar una entrada sin dispararla al importar el módulo.

Un paquete reúne módulos y declara una interfaz. Evite imports circulares y efectos externos al importar. Los ejemplos del aula escriben módulos reales en el sistema virtual; descargue sus archivos para usarlos en un proyecto local.

- Usar un namespace: Importe math y calcule raiz=math.sqrt(81).
- Importar con alias: Importe statistics como st y calcule media de [2,4,6].
- Escribir un módulo: Cree utilcurso.py con doble(x) que devuelva x*2 e impórtelo como utilcurso.
- Separar entrada principal: Cree entrada.py con def main(): return 0 y guard __name__ que llame main().
- Inspeccionar una interfaz: Con math, cree disponibles como sorted de nombres de dir(math) que no empiezan por _.

Referencia: https://docs.python.org/3.13/tutorial/

## 26. Anotaciones de tipo y contratos

Use tipos para comunicar intención y validar valores donde corresponde.

Las anotaciones describen tipos esperados; Python no las impone automáticamente durante ejecución. list[float], dict[str,int] y float | None expresan estructuras frecuentes. Un verificador estático como mypy o pyright analiza estas declaraciones fuera del aula.

La validación de valores sigue siendo necesaria. Distinga un dato opcional de uno vacío, evite valores predeterminados mutables y documente excepciones. Un contrato incluye el dominio del valor, no solo su clase.

- Anotar una función: Defina doble(x:float)->float que devuelva x*2.
- Representar ausencia: Defina limpiar(x:str|None)->str que devuelva "" para None y x.strip() en otro caso.
- Validar el dominio: Defina finito(x) que rechace números no finitos con ValueError y devuelva float(x).
- Evitar predeterminados mutables: Defina agregar(x,items=None) que cree lista si falta, agregue x y la devuelva.
- Documentar excepciones: Defina dividir(a,b) con docstring que incluya ZeroDivisionError y devuelva a/b.

Referencia: https://docs.python.org/3.13/library/typing.html

## 27. Pruebas unitarias, propiedades y dobles

Verifique comportamientos y casos límite con evidencia reproducible.

unittest y pytest organizan pruebas en suites. Una prueba debe distinguir la implementación correcta de errores plausibles. Incluya entradas vacías, límites y excepciones; para decimales use tolerancia apropiada.

Los mocks sustituyen una dependencia externa para observar un contrato, pero no prueban que la integración real funcione. Separe funciones puras y efectos; conserve una prueba de integración cuando cambie la frontera entre componentes.

- Probar con unittest: Cree clase Prueba(unittest.TestCase) con test_media que compruebe statistics.mean([2,4])==3; ejecute su suite en resultado.
- Probar una excepción: Con unittest.TestCase(), compruebe que int("x") lanza ValueError; marque verificado=True.
- Comparar decimales: Cree iguales=math.isclose(.1+.2,.3,rel_tol=1e-9).
- Sustituir una dependencia: Cree cliente=Mock(return_value={"ok":True}); llámelo con "A" y compruebe assert_called_once_with("A").
- Probar una propiedad: Defina propiedad(xs) que compruebe list(reversed(list(reversed(xs))))==xs.

Referencia: https://docs.python.org/3.13/library/unittest.html

## 28. Expresiones regulares, fechas y zonas

Interprete texto y tiempo con reglas explícitas.

re.fullmatch exige que toda la cadena cumpla un patrón. Los grupos capturan componentes; re.sub sustituye coincidencias. Use cadenas raw para patrones con barras invertidas y evite patrones ambiguos sobre entradas enormes.

datetime distingue objetos con y sin zona horaria. Para intercambiar instantes use UTC explícito; para fechas de calendario use date. Un cambio de horario puede alterar la duración real entre dos horas locales.

- Validar identificadores: Defina valido(s) que acepte M seguido de uno o más dígitos.
- Extraer campos: De "Lote-2026-15", extraiga anio y numero como enteros con re.fullmatch.
- Leer una fecha: Cree fecha a partir de 19/10/2026 y calcule dia=fecha.day.
- Calcular intervalos: Calcule dias entre date(2026,10,1) y date(2026,10,19).
- Crear un instante UTC: Cree instante=datetime(2026,10,19,12,0,tzinfo=timezone.utc).

Referencia: https://docs.python.org/3.13/tutorial/

## 29. JSON y bases de datos SQL

Intercambie registros y consulte tablas separando datos de instrucciones.

json.dumps serializa valores compatibles; json.loads interpreta texto. JSON no conserva automáticamente clases, fechas ni tuplas. Valide estructura y tipos tras leer una respuesta externa.

sqlite3 permite practicar SQL localmente. Use parámetros ? en execute y pase los valores aparte. Una transacción agrupa cambios; las claves y restricciones hacen que las invariantes no dependan solo del código cliente.

- Codificar y leer JSON: Cree texto JSON de {"id":"A","valor":2} y copia con json.loads.
- Crear una tabla SQL: Cree con=sqlite3.connect(":memory:") y tabla muestras(id TEXT PRIMARY KEY,valor REAL).
- Insertar con parámetros: En SQLite cree muestras e inserte ("A",2.5) mediante placeholders; guarde fila consultada.
- Agrupar con SQL: Con filas ("C",2),("C",4),("T",8), calcule resumen SQL con AVG(valor) por grupo, ordenado por grupo.
- Revertir una transacción: Cree tabla t(x), confirme esquema, inserte 1 y haga rollback; guarde n=COUNT(*).

Referencia: https://docs.python.org/3.13/library/sqlite3.html

## 30. HTTP, APIs y validación de entradas

Diseñe un cliente que pueda fallar de manera comprensible.

Un contrato HTTP incluye método, URL, estado, contenido y límites de tiempo. Separe transporte y validación para poder probar errores sin depender de una red externa. Reintente solo operaciones compatibles con una política de idempotencia.

El aula valida respuestas de ejemplo y prepara contratos; no promete acceso a servicios externos. No guarde secretos en código ni acepte rutas arbitrarias. Los datos externos requieren validación, incluso si el servidor respondió 200.

- Codificar parámetros: Cree query de {"grupo":"control A","n":5} con urllib.parse.urlencode.
- Validar una respuesta: Defina leer(estado,cuerpo) que rechace estado distinto de 200 con ValueError y devuelva json.loads(cuerpo).
- Validar esquema: Defina validar(x) que devuelva True solo para dict con id:str y valor:int o float finito, excluyendo bool.
- Limitar reintentos: Defina intentar(fn,n=3) que pruebe hasta n veces ante ValueError y vuelva a lanzar al agotarse.
- Confinar rutas: Defina ruta_segura(base,nombre) con pathlib, rechace si la ruta resuelta no queda dentro de base.

Referencia: https://docs.python.org/3.13/tutorial/

## 31. Programación asíncrona con asyncio

Coordine esperas sin bloquear el hilo que atiende otras tareas.

async def crea una función que devuelve una coroutine; await espera su resultado. asyncio.gather coordina varias operaciones y conserva el orden de entrada en sus resultados. La concurrencia de esperas no equivale a paralelismo de CPU.

El aula admite await en el nivel superior. Las tareas de ejemplo usan asyncio.sleep con duración cero y no realizan red externa. En producción defina cancelación, límites de tiempo y limpieza de recursos; no use asyncio.run dentro de un bucle ya activo.

- Esperar una coroutine: Defina async doble(x) y guarde resultado=await doble(3).
- Ceder al bucle: Defina async tarea(x) que espere asyncio.sleep(0) y devuelva x+1; resultado=await tarea(4).
- Coordinar resultados: Defina async doble(x) y resultados=await asyncio.gather(doble(2),doble(3)).
- Crear una tarea: Cree tarea=asyncio.create_task(asyncio.sleep(0,result=7)) y resultado=await tarea.
- Limitar una espera: Use await asyncio.wait_for(asyncio.sleep(0,result="ok"),timeout=1) en resultado.

Referencia: https://docs.python.org/3.13/library/asyncio.html

## 32. Concurrencia, tareas y límites del navegador

Elija un modelo de ejecución según el trabajo y el entorno.

Los hilos sirven para coordinar ciertas tareas de entrada/salida; los procesos permiten aislar trabajo de CPU. El coste de serialización y el estado compartido condicionan el rendimiento. No asuma que un programa gana velocidad al añadir trabajadores.

Pyodide tiene restricciones de procesos e hilos. Aquí se practican tareas asíncronas, colas y límites de concurrencia reales; multiprocessing y ProcessPoolExecutor se estudian como un archivo para ejecutar en su instalación local, no se simula su ejecución.

- Usar una cola asíncrona: Cree asyncio.Queue, añada A y B con await put y retire primero con await get.
- Limitar concurrencia: Con semaforo=asyncio.Semaphore(2), defina async trabajo(x) usando async with y devuelva x*x; reúna 1,2,3 en resultados.
- Coordinar con TaskGroup: Defina async uno() que devuelva 1; cree tarea en async with asyncio.TaskGroup() y resultado=tarea.result() al salir.
- Reunir errores explícitos: Defina async falla() que lance ValueError y use gather con return_exceptions=True en resultados.
- Preparar procesamiento local: Cree paralelo.py con ProcessPoolExecutor y guard __main__.

Referencia: https://docs.python.org/3.13/library/asyncio-task.html

## 33. Algoritmos, búsqueda y complejidad

Elija recorridos que escalen con la estructura de sus datos.

La notación O describe crecimiento del coste, no segundos concretos. Una búsqueda lineal recorre n elementos; una búsqueda binaria reduce el intervalo en datos ordenados; un diccionario ofrece búsqueda promedio eficiente bajo sus supuestos.

Primero garantice corrección y casos límite. El algoritmo debe declarar si modifica su entrada, si necesita orden y cómo maneja duplicados. Use estructuras estándar y mida casos representativos antes de optimizar.

- Buscar linealmente: Defina buscar(xs,objetivo) que devuelva primer índice o -1.
- Buscar en datos ordenados: Use bisect_left sobre [1,3,3,7] para objetivo 3 en posicion.
- Eliminar duplicados conservando orden: Defina unicos(xs) que preserve primera aparición.
- Recorrer un grafo: Defina bfs(g,inicio) que devuelva orden BFS; el grafo usa dict de listas.
- Programación dinámica: Defina fibonacci(n) iterativo con F0=0,F1=1.

Referencia: https://docs.python.org/3.13/tutorial/

## 34. NumPy: formas, broadcasting y álgebra

Comprenda las formas antes de interpretar una operación vectorizada.

Un ndarray combina tipo homogéneo, forma y strides. reshape cambia interpretación de dimensiones cuando el número de elementos coincide. El broadcasting alinea dimensiones desde el final: cada par debe ser igual o contener un uno.

Una vista puede compartir memoria con su origen; una copia es independiente. axis identifica la dimensión que se reduce. Para sistemas lineales use solve y revise rango y estabilidad en vez de construir inversas manualmente.

- Reorganizar una matriz: Cree a=np.arange(12).reshape(3,4).
- Aplicar broadcasting: Cree a=np.array([[1,2],[3,4]]) y salida=a+np.array([10,20]).
- Reducir un eje: Con a=np.arange(6).reshape(2,3), cree medias por columna.
- Separar una copia: Cree original=np.array([1,2,3]), copia=original.copy(); cambie copia[0]=99.
- Resolver un sistema: Resuelva A=[[2,0],[0,4]],b=[6,8] en solucion.

Referencia: https://numpy.org/doc/stable/user/basics.broadcasting.html

## 35. pandas: joins, pivotes y ventanas

Haga explícitas claves, cardinalidad, orden y unidad del resumen.

merge alinea tablas por claves; validate comprueba cardinalidad. Una unión con claves duplicadas puede multiplicar filas. pivot exige una celda única por combinación; pivot_table agrega si hay duplicados y esa elección debe justificarse.

Las ventanas móviles requieren un orden definido. groupby.transform devuelve un resultado alineado con las filas originales; agg reduce grupos. Compruebe filas, claves ausentes y tipos después de cada transformación.

- Unir con validación: Una a con id A,B y x 1,2; b con id B,A y y 20,10; cree unidos por id con validate="one_to_one".
- Pivotar una tabla: De id A,A; dia d1,d2; valor 2,4, cree ancho con index=id,columns=dia,values=valor.
- Transformar por grupo: Con grupo C,C,T,T y valor 2,4,6,10, cree centrado restando la media de su grupo.
- Calcular una ventana: De pd.Series([2,4,6,8]), cree movil de ventana 2 y min_periods=2.
- Auditar claves huérfanas: Haga left merge de a id A,B con b id A usando indicator=True y guarde huerfanas como lista de ids left_only.

Referencia: https://pandas.pydata.org/docs/user_guide/merging.html

## 36. SciPy: optimización, integración y pruebas

Combine métodos numéricos con comprobaciones de convergencia y supuestos.

SciPy ofrece optimización, integración, álgebra y distribuciones. Un resultado de optimización incluye estado de convergencia: conservar solo el vector de parámetros puede ocultar un fallo. Escalas e inicialización afectan el algoritmo.

Una prueba estadística necesita un diseño y supuestos. La simulación y los métodos numéricos pueden comprobar una implementación; no validan por sí mismos que los datos respondan la pregunta científica.

- Optimizar un escalar: Cree ajuste de (x-3)**2 con bounds=(-10,10),method="bounded".
- Integrar una función: Guarde integral,error de quad(lambda x:x*x,0,1).
- Encontrar una raíz: Resuelva x*x-2=0 en [0,2] en raiz.
- Comparar con Welch: Calcule prueba=ttest_ind([1,2,4,5],[3,5,7,9],equal_var=False).
- Trabajar con distribuciones: Calcule probabilidad=scipy.stats.norm.cdf(1.96).

Referencia: https://docs.scipy.org/doc/scipy/tutorial/optimize.html

## 37. Machine learning: partición, pipelines y métricas

Estime desempeño sobre observaciones que no participaron en el ajuste.

La partición debe respetar sujeto, grupo o tiempo. Un pipeline ajusta escalado y modelo dentro del entrenamiento para reducir fuga de información. La prueba final queda reservada hasta decidir el procedimiento.

Un baseline da contexto a la métrica. La exactitud puede ocultar desbalance; una matriz de confusión distingue errores. Los ejercicios usan scikit-learn real cuando se importa y datos de práctica; no equivalen a un modelo validado para uso clínico.

- Partir sin superposición: Divida np.arange(20) con test_size=.25,random_state=42 en train,test.
- Escalar sin fuga: Ajuste StandardScaler a [[1],[2],[3]] y transforme [[4],[5]] en ztest.
- Construir un pipeline: Cree modelo=make_pipeline(StandardScaler(),LogisticRegression(random_state=42)); ajuste iris.data,iris.target y guarde predicciones.
- Evaluar por validación cruzada: Con iris, cree puntuaciones=cross_val_score(make_pipeline(StandardScaler(),LogisticRegression(max_iter=300)),iris.data,iris.target,cv=3).
- Calcular confusión: Cree matriz=confusion_matrix([0,0,1,1],[0,1,1,1],labels=[0,1]).

Referencia: https://scikit-learn.org/stable/common_pitfalls.html

## 38. Series temporales y visualización avanzada

Respete el orden y las unidades al transformar datos de tiempo.

pandas representa índices temporales, remuestreo y ventanas. Un resample necesita conocer la frecuencia y el significado de la agregación. Una media diaria y un total diario responden preguntas distintas.

Una figura debe comunicar unidades, origen y tratamiento de ausencias. Para evaluar pronósticos reserve observaciones posteriores y compare contra un baseline temporal. No use el futuro para completar características del pasado.

- Crear un índice temporal: Cree fechas=pd.date_range("2026-01-01",periods=4,freq="D").
- Remuestrear observaciones: Cree serie de [1,3,5,7] cada 12 horas desde 2026-01-01; calcule diaria como media por día.
- Crear un retardo: Con serie=pd.Series([2,4,6]), cree anterior=serie.shift(1).
- Diseñar paneles: Cree fig,axes=plt.subplots(1,2); dibuje línea [1,2,3] en primero e histograma [1,1,2,3] en segundo; etiquete ambos.
- Evaluar un baseline temporal: Con train=[2,3,5],test=[6,8], prediga 5 dos veces y calcule rmse.

Referencia: https://docs.python.org/3.13/tutorial/

## 39. CLI, configuración, logging y distribución

Convierta funciones útiles en una herramienta que otra persona pueda ejecutar.

argparse define una interfaz de línea de comandos; logging registra eventos con severidad sin mezclar todo con resultados impresos. La configuración externa evita constantes escondidas; los secretos deben quedar fuera del repositorio.

pyproject.toml declara metadatos y herramientas de construcción; un entorno virtual separa dependencias. Git, pip, publicación de paquetes y CI se ejecutan en su computadora o servicio de integración. El aula genera archivos descargables y prueba componentes puros del CLI.

- Interpretar argumentos: Cree parser con --factor float predeterminado 2 y args=parser.parse_args(["--factor","3.5"]).
- Registrar un evento: Cree buffer=io.StringIO y logger sin propagación, registre INFO "Proceso completo" en buffer.
- Declarar metadatos: Escriba pyproject.toml con [project],name="curso-demo",version="0.1.0",requires-python=">=3.13"; léalo en config.
- Registrar dependencias: Escriba entorno.json con python=sys.version.split()[0] y plataforma="Pyodide".
- Preparar integración continua: Cree ci.yml con pasos python -m pip install -e . y python -m unittest discover.

Referencia: https://docs.python.org/3.13/tutorial/

## 40. Proyecto final: pipeline de datos con entrega

Integre lectura, validación, análisis, pruebas y un informe descargable.

El proyecto usa un conjunto sintético y define una observación independiente por id. La entrega debe conservar el CSV original, reglas de calidad, resumen, figura y metadatos de ejecución. No convierta un resultado de práctica en una recomendación real.

Cada hito contiene sus entradas y se revisa desde un namespace limpio. Así puede reconstruirse por separado. Al terminar descargue el script y los archivos; el avance local registra qué ejercicios aprobó, sin representar una certificación externa.

- Importar y validar: Cree datos desde CSV id,grupo,valor con A,C,2;B,C,4;C,T,5;D,T,7; exija id único y sin NA.
- Generalizar un efecto: Defina efecto(control,tratado) como diferencia de medias tratado-control.
- Crear un resumen exportable: Con grupos C,C,T,T y valores 2,4,5,7, cree resumen con columnas grupo,n,media y guarde entrega.csv sin índice.
- Entregar una figura: Cree figura.png con barras C,T de alturas 3,6; etiquete y como Media y título Datos sintéticos.
- Documentar la entrega: Cree informe.json con origen="sintetico",metodo="diferencia de medias",efecto=3 y causalidad=False; cree README.md que incluya Reproducción y Limitaciones.

Referencia: https://docs.python.org/3.13/tutorial/

