Los type hints, o anotaciones de tipo, permiten describir qué datos espera y devuelve el código Python. No convierten a Python en un lenguaje de tipado estático ni validan automáticamente todos los valores, pero mejoran autocompletado, documentación, revisión y detección temprana de errores.
Primeras anotaciones
Una función puede anotar parámetros y retorno:
def saludar(nombre: str, veces: int = 1) -> str:
return " ".join([f"Hola, {nombre}"] * veces)Los editores pueden advertir si alguien pasa una lista donde se esperaba una cadena. Para repasar la estructura, consulta la guía de funciones en Python.
Tipos básicos y colecciones
Se pueden anotar str, int, float, bool y colecciones:
def promedio(valores: list[float]) -> float:
return sum(valores) / len(valores)
def contar(textos: tuple[str, ...]) -> dict[str, int]:
return {texto: len(texto) for texto in textos}Las formas modernas como list[str] funcionan en versiones recientes de Python. Proyectos compatibles con versiones antiguas pueden utilizar equivalentes del módulo typing.
Optional y valores None
Si un valor puede ser una cadena o None, exprésalo:
def buscar_usuario(usuario_id: int) -> str | None:
if usuario_id == 1:
return "Ana"
return NoneEsto obliga a quien llama a considerar la ausencia de resultado. La guía sobre return en Python explica cómo diseñar resultados previsibles.
Union y alias de tipo
Un valor puede aceptar más de un tipo:
Identificador = int | str
def normalizar_id(valor: Identificador) -> str:
return str(valor).strip()Los alias mejoran legibilidad cuando una combinación se repite. No crees alias para conceptos triviales; úsalos cuando expresen una idea del dominio.
TypedDict y datos estructurados
Para diccionarios con claves conocidas, TypedDict es más preciso que dict[str, object]:
from typing import TypedDict
class Usuario(TypedDict):
nombre: str
edad: int
activo: bool
def mostrar(usuario: Usuario) -> str:
return f"{usuario['nombre']} - {usuario['edad']}"En estructuras más complejas también conviene considerar dataclasses o modelos de validación.
Callable para funciones
Cuando una función recibe otra función, Callable describe la firma:
from collections.abc import Callable
def aplicar(
operacion: Callable[[int, int], int],
a: int,
b: int
) -> int:
return operacion(a, b)Este patrón aparece en callbacks y decoradores. La guía de decoradores en Python muestra casos donde las anotaciones se vuelven especialmente útiles.
Genéricos con TypeVar
Un genérico conserva la relación entre entrada y salida:
from typing import TypeVar
T = TypeVar("T")
def primero(elementos: list[T]) -> T:
if not elementos:
raise ValueError("La lista está vacía")
return elementos[0]Si se recibe una lista de cadenas, el resultado se interpreta como cadena. Si se recibe una lista de enteros, el resultado es entero.
Herramientas de análisis
Los type hints ofrecen más valor cuando se ejecuta un analizador como mypy, Pyright o el sistema integrado del editor. Estas herramientas detectan retornos incompatibles, atributos inexistentes y argumentos incorrectos sin ejecutar el código.
La documentación oficial del módulo typing presenta las herramientas disponibles. El PEP 484 describe el sistema original de type hints.
Type hints no son validación
Python normalmente no impide esta llamada en tiempo de ejecución:
def duplicar(numero: int) -> int:
return numero * 2
duplicar("hola") # produce "holahola"La anotación informa, pero no valida. En límites externos, como una API o un archivo, utiliza validación explícita o bibliotecas adecuadas. Por ejemplo, FastAPI usa anotaciones junto con Pydantic para validar solicitudes.
Type hints con *args y **kwargs
También pueden anotarse argumentos flexibles:
def sumar(*valores: float) -> float:
return sum(valores)
def configurar(**opciones: str) -> dict[str, str]:
return opcionesLa guía de *args y **kwargs explica la mecánica de estos parámetros. La anotación se aplica a cada valor capturado, no a la tupla o diccionario completo de la firma.
Buenas prácticas
Anota las interfaces públicas, funciones reutilizables y estructuras importantes. Evita tipos demasiado generales como Any cuando puedes ser específico. No añadas anotaciones tan complejas que nadie pueda comprenderlas. El objetivo es aclarar el código, no demostrar sofisticación.
Adopta type hints de forma gradual. Empieza por parámetros y retornos, configura un analizador y corrige advertencias relevantes. Las anotaciones bien diseñadas funcionan como documentación ejecutable para herramientas y ayudan a que refactorizaciones grandes sean más seguras.
Protocolos y tipado estructural
Un Protocol describe comportamientos sin exigir herencia directa. Esto es útil cuando varias clases poseen el mismo método, aunque no comparten una clase base.
from typing import Protocol
class Exportable(Protocol):
def exportar(self) -> str:
...
def guardar(objeto: Exportable) -> str:
return objeto.exportar()Cualquier objeto con un método compatible puede utilizarse. Este enfoque reduce acoplamiento y encaja con el estilo flexible de Python.
ParamSpec en decoradores
Tipar un decorador genérico es difícil porque debe conservar los parámetros de la función original. ParamSpec representa esa lista de parámetros y permite que el analizador mantenga la firma. No es necesario para empezar, pero resulta valioso en bibliotecas y utilidades compartidas.
Anotaciones y versiones de Python
Las capacidades de typing evolucionan. Antes de adoptar una sintaxis, revisa la versión mínima del proyecto. Un equipo que soporta versiones antiguas puede necesitar importar tipos desde typing_extensions. Mantén la configuración del analizador alineada con la versión ejecutada en producción para evitar advertencias engañosas.






