16 · Funciones: definir, llamar, parámetros, retorno
Escribe bloques reutilizables y testeables — parámetros, valores por defecto, retorno, scope y la trampa del argumento mutable que todo desarrollador Python toca una vez.
Una función es un bloque de código nombrado y reutilizable — `def nombre(params): ...` — que acepta entradas, realiza trabajo y opcionalmente devuelve un valor. Las funciones son la unidad de abstracción en Python.
Sin esto:
Sin funciones, todo cálculo repetido debe copiarse y pegarse — cambiar la fórmula en un lugar no arregla las copias, los errores se multiplican y los experimentos se vuelven irreproducibles porque la misma lógica corre de forma ligeramente diferente en dos lugares.
Define una función con def nombre(parámetros): seguido de un cuerpo indentado. Llámala escribiendo nombre(argumentos). Sin una sentencia return (o con return solo) la función devuelve None. Los parámetros son posicionales por defecto; también puedes pasarlos por nombre como argumentos de palabra clave: fn(x=1, y=2). Los valores por defecto permiten hacer parámetros opcionales — pero nunca uses un objeto mutable (lista, dict, set) como valor por defecto, porque Python lo crea una sola vez en el momento de definir la función y reutiliza el mismo objeto en cada llamada.
Python (in browser)
Una función con docstring y un valor de retorno claro es inmediatamente testeable — puedes llamar `mse([1,2],[1,2])` y verificar que devuelve `0.0` sin ejecutar todo un pipeline.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Python (in browser)
Las tres llamadas comparten la misma lista por defecto. Esta es la trampa más famosa de Python — `history=[]` se evalúa una sola vez cuando se ejecuta la sentencia `def`, no en cada llamada.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
Python (in browser)
La solución canónica: usa `None` como valor por defecto y crea el objeto mutable dentro del cuerpo de la función. Cada llamada obtiene su propia lista fresca.
Python runs entirely in your browser via Pyodide (~6 MB on first Run, cached after).
LEGB es el orden en que Python busca un nombre: Local → Enclosing → Global → Built-in. `global` y `nonlocal` permiten salir del scope local para mutar nombres más arriba en la cadena.
¿Qué devuelve `def f(): pass` cuando se llama como `f()`?
Implementa funciones de métricas, corrige errores de argumento mutable y traza el scope en el Arena con tests del lado del servidor.
- `def nombre(params): ... return valor` — la anatomía completa. Sin `return`, la función devuelve `None`.
- Nunca uses un objeto mutable (`[]`, `{}`, `set()`) como parámetro por defecto. Usa `None` y crea el objeto dentro del cuerpo.
- Python resuelve nombres en orden LEGB. Usa `global` o `nonlocal` solo cuando necesites mutar un nombre de scope externo — prefiere devolver valores.
Toda función de pérdida, métrica y transformación de datos en un codebase de ML es una función de Python: `mse(y_true, y_pred)`, `accuracy(y_true, y_pred)`, `normalize(x, mean, std)`. Los experimentos reproducibles dependen de funciones puras — las mismas entradas siempre producen las mismas salidas, sin estado oculto mutado por argumentos mutables.
Si lo quitas: Sin funciones, los cálculos de pérdida son expresiones inline repetidas en los bucles de entrenamiento, validación y test — cambiar la fórmula requiere encontrar y actualizar cada copia, y una actualización omitida evalúa silenciosamente una métrica diferente en cada split.