En el mundo del desarrollo de software, los pequeños detalles generan grandes diferencias. Incorporar buenas prácticas como el uso de type hints en Python puede parecer un cambio menor, pero tiene un impacto directo en la calidad del código, la eficiencia del equipo y la escalabilidad del negocio.
Menos errores, más legibilidad, mejor colaboración entre desarrolladores: todo esto se traduce en menos tiempo perdido, menos deuda técnica y más valor entregado.
En esta nota te vamos a contar acerca de una herramienta muy útil para corregir nuestro código, evitar errores y garantizar que cualquier persona que venga detrás nuestro y lea nuestro código no se agarre la cabeza. ¡Vamos allá!
O sea, tipo, nada
Python es un lenguaje de tipado dinámico, lo que significa que las variables pueden ir cambiando su tipo de dato a lo largo de la ejecución de un script, a diferencia de lenguajes con tipado estático en los que declaramos previo a la compilación del código de qué tipo es cada una. Esto lo hace más flexible y brinda más posibilidades, pero también lo hace más propenso a ciertos errores.
¿A qué nos referimos con esto? Esta es una función llamada multi_call que tenemos definida en un archivo llamado multi.py.

Es una función totalmente normal: toma dos argumentos, num y alt, y alt tiene un valor por defecto que es igual a False. Si alt es igual a True, el output de nuestra función será num multiplicado por 5. Si dejamos el valor default de alt, la ejecución devolverá num multiplicado por -2.
Cuando definimos un argumento de una función, si no explicitamos el tipo de dato que debe ser, el intérprete de Python identificará que se trata de una variable de tipo “Any”; es decir, acepta cualquier tipo de dato como posible input.

En el caso de esta función, al setear nosotros un valor predeterminado para el argumento alt de tipo booleano, Python esperará un dato del mismo tipo, por lo que aceptaría valores de True o False pero no 1, 3.7 o “true”. Pero, ¿qué pasa si nosotros no queremos indicar valores por default? ¿Cómo podemos ayudar a que nuestro código sea más legible y menos propenso a errores y bugs?
Existen algunas herramientas y buenas prácticas que nos ayudan a definir y explicitar el tipo de datos que asignamos a cada variable como una instancia adicional de control. Una de ellas es el type hint.
Te doy una pista…
Cuando indicamos que el valor predeterminado de alt es False, el intérprete de Python determina que es un argumento que toma valores booleanos. alt=False es equivalente a escribir alt: bool = False y, en esta segunda sintaxis, lo que estamos diciendo es “el argumento alt toma valores booleanos y tiene por defecto el valor False”. En versiones modernas de Python (3.10 en adelante), también podemos usar una sintaxis más concisa con el operador | para definir tipos opcionales: por ejemplo, alt: bool | None si aceptáramos también valores nulos.
Esto es útil para eliminar la ambigüedad y prevenir errores en la ejecución, y es algo que podemos hacer de manera explícita con todos los parámetros que toma una determinada función. A partir de Python 3.9, incluso podemos usar una sintaxis más simple y nativa como list[int] o dict[str, float], sin necesidad de importar List o Dict desde typing.
Observemos la función multi_call() actualizada con más información:

Aquí, se ve claramente que el desarrollador que escribió esta función informó que el argumento num debe tomar valores de números enteros, una definición que es lógica considerando que eso luego se multiplicará por 5 o -2 de acuerdo a la condición booleana que impone el argumento alt. Pero además, agregamos por fuera de los paréntesis que indican los argumentos de la función un -> int, que aclara el tipo de dato del output de nuestra función.
Estas “ayuditas” que le estamos dando a nuestra función se conocen como “type hints”, pistas de datos, y son una excelente herramienta para mantener nuestro código prolijo, legible y libre de bugs. Fueron presentados en el PEP 484 (Python Enhancement Proposal, un documento con información para la comunidad Python que propone mejoras o describe nuevas funcionalidades o procesos) y se han convertido en práctica habitual en todo tipo de desarrollos en este lenguaje.
Una de las ventajas de esto es poder hacer “hover” por encima de una variable antes de su asignación y que el intérprete nos indique con anticipación de qué tipo va a ser la variable resultante. Esto en una función simple como la que estamos analizando parece obvio y evidente, pero en otras más complejas nos puede ahorrar bastante tiempo de análisis de código y tipos de datos.
Ahora, si nosotros llamamos a multi_class y para el argumento num pasamos 7.2, es decir, si asignamos un float en vez de un int, la función va a ejecutar igual. Ya se escucha tu frustración del otro lado de la pantalla. ¿Por qué, si nosotros especificamos el tipo con un type hint? Porque los hints no fuerzan el tipo de dato que toma la función sino que facilitan su utilización a nuestro usuario. Si quisiéramos frenar una ejecución que no se ajusta a los hints que nosotros le dimos, deberíamos usar una sintaxis similar a la siguiente que extienda nuestro código:

En esta función modificada, es la declaración assert la que nos valida el tipo de dato.
Entonces, ya sabemos que los type hints son una buena práctica para la legibilidad y debuggeo de nuestro código. Es momento de presentarles una herramienta que nos facilitará incluso más nuestra vida y la de toda la gente que tenga que trabajar con estos scripts.
Si te gusta el durazno, no te banques la pelusa
Antes de avanzar con lo que resta de esta nota, me gustaría hacer un breve paréntesis para contarles acerca de los linters, un componente muy útil y en muchos casos crucial para garantizar la legibilidad, calidad y adhesión a estándares de calidad y buenas prácticas de nuestro código.
Un linter (“quitador de pelusas” en inglés, aunque toma su nombre de una utility de Unix usada para encontrar errores en código escrito en C) es una herramienta que podemos incorporar a nuestro IDE para ayudar al desarrollo de código mediante la detección de posibles errores, bugs, inconsistencias de estilo y otros problemas potenciales. Existen linters de distinto tipo y para encontrar errores de lo más variados; queda en cada developer encontrar el que más le guste o más se ajuste a su caso de uso.
Veamos, entonces, una librería especialmente diseñada para automatizar el chequeo de type hints: MyPy. La idea de este linter es que incorpora elementos de un lenguaje de tipado estático a Python para la prueba de las funciones que definimos en nuestro código por medio de controlar que los tipos de datos que declaramos como type hints coincidan con las variables que se llaman más adelante. Esto nos permite, sin necesidad de ejecutar los scripts, garantizar el tipado consistente de las variables y los outputs de nuestras funciones.
La instalación se puede hacer con el comando !pip install, como casi cualquier librería estándar.

Es importante destacar que MyPy no funciona para Jupyter Notebooks (una de las opciones predilectas por data scientists para “mockear” o prototipar scripts), pero sí para analizar archivos enteros.
Aquí abajo vemos el ejemplo de classes.py:
- Tenemos un __init__ que toma tres argumentos: a, b y c, con tipos int, str y float respectivamente.
- Hemos parametrizado a como 1, b como 1 y c como “asdad” (también conocido como “cabezazo en el teclado”).
- El tipo de dato que informamos a nuestro parámetro a es correcto; 1 es un int. Pero b y c fueron declarados con tipo de datos incorrectos de acuerdo a lo que nosotros especificamos como type hint. Si observan en detalle, vemos que cuando tenemos a MyPy seleccionado como linter, en nuestra asignación de A se nos resaltan los parámetros b y c con una pequeña línea roja abajo que indica un error.

Recordemos nuevamente en esta instancia que pasar parámetros a nuestras funciones con tipos de datos distintos a los que indicamos como type hints no implica que su ejecución vaya a ser errónea. Cuando corregimos los tipos de datos que pasamos como parámetros para que coincidan con lo que indicamos, las líneas rojas debajo de los argumentos desaparecen.
Ahora, veamos de qué manera podemos analizar el archivo donde tenemos escrita nuestra clase con MyPy.

Si llamamos al archivo multi.py que contiene multi_call (que, dicho sea de paso, se encuentra en el mismo directorio que esta notebook que estamos corriendo) con el comando !mypy, vemos que en el output de esta celda la librería detectó inmediatamente las inconsistencias de las que venimos hablando.
Otra opción que tenemos es llamar directamente !mypy y eso analizará todos los archivos que tenemos en el directorio que terminan en .py.

Pero me quedé con ganas de más…
Hasta acá llega esta primera nota. Vimos cómo el uso de type hints y herramientas como MyPy no solo mejora la calidad del código, sino que también impacta en la eficiencia del equipo, reduce errores evitables y facilita el mantenimiento de proyectos en crecimiento. En contextos donde el software es parte del core del negocio, escribir código más claro, legible y controlado no es solo una cuestión técnica, sino una decisión estratégica.
¿Y si además de sugerir tipos, pudiéramos validarlos automáticamente en tiempo de ejecución? Existe una librería pensada exactamente para eso… pero eso te lo contamos en la próxima entrega.


