Funciones con un contrato
Dale a cada comportamiento un nombre, entradas, una salida y una promesa.
AL TERMINAR, PODRÁS
- Diferenciar un valor de retorno de lo que se imprime en pantalla
- Escribir una función con entradas explícitas
- Explicar precondiciones, postcondiciones y efectos secundarios
Descripción del diagrama: attempt: int → retry_delay() (entrada); retry_delay() → seconds: float (devuelve); retry_delay() → ValueError (lanza); seconds: float → sleep, log (quien llama actúa)
Una frontera útil
Una función empaqueta un comportamiento detrás de un nombre.
Su contrato explica qué entradas acepta, qué devuelve y qué estado modifica. Empieza con un cálculo puro, como la espera antes de un reintento: las mismas entradas producen la misma salida, sin llamadas de red ni estado oculto. Quien llama a la función puede usar el valor devuelto (return) en otro cálculo. print solo muestra un valor; no se lo devuelve a quien llamó.
Las funciones sin un return explícito devuelven None (el valor «nada» de Python).
def retry_delay(attempt):
if attempt < 0:
raise ValueError("attempt must be nonnegative")
return 60 if attempt >= 6 else 2 ** attempt
print(retry_delay(3))
Haz que las suposiciones se puedan revisar
Una precondición describe lo que debe cumplirse antes de que la función se ejecute.
Una postcondición describe lo que la función garantiza después. Nuestra función de espera exige un número de intentos no negativo y garantiza una espera de sesenta segundos como máximo. Compara el intento con seis antes de calcular 2 ** attempt, así que un número de intentos enorme nunca construye un número gigante.
Decide si una entrada inválida se rechaza, se convierte o se trata de forma especial; las suposiciones silenciosas suelen esconder errores. Las anotaciones de tipo (type hints) ayudan a las herramientas y a quien lee a entender la intención, pero las anotaciones normales de Python no validan nada mientras el programa corre. Un nombre de función preciso suele comunicar más que un comentario largo.
Separa las decisiones de los efectos
El código que decide si reintentar es más fácil de probar por separado del código con efectos secundarios (side effects: cambios que van más allá del valor devuelto), como esperar, escribir un archivo o enviar un correo.
Deja la decisión en una función pequeña y ejecuta el efecto en una frontera exterior. Eso no significa que cada línea necesite su propia función: extrae una frontera cuando nombre un concepto con significado o aísle una incertidumbre. Evita los argumentos por defecto mutables para estado de cada llamada: Python crea los objetos de los argumentos por defecto una sola vez, cuando se define la función, y eso puede compartir datos entre llamadas sin querer.
ERROR FRECUENTE
Un valor por defecto como jobs=[] comparte la misma lista entre llamadas. Usa None y crea una lista nueva dentro de la función cuando quieras estado independiente.
INTÉNTALO TÚ
Escribe eligible(status, attempts, limit) que devuelva un Boolean. Especifica su contrato y luego prueba trabajos completados, cero intentos e intentos exactamente iguales al límite.
Decide en el laboratorio.
El checkpoint de dos preguntas, tus notas y tu progreso guardado están en el laboratorio. Gratis y sin cuenta.
Abrir en el laboratorio ↗CONÉCTALO CON RELAY
Extrae la decisión de reintento de Relay y el cálculo de la espera a funciones que no lean estado global.