get_type_hints en Python: lee anotaciones

Publicado el: 29/08/2026
Tempo de leitura: 5 minutos
A person typing on a laptop with a Python programming book visible, capturing technology and learning.

typing.get_type_hints() recupera anotaciones de funciones, métodos, clases y módulos en una forma más útil que leer directamente __annotations__. Puede resolver referencias futuras, combinar anotaciones heredadas y conservar metadatos de Annotated cuando se solicita.

Ese poder requiere cuidado porque la resolución depende de namespaces y puede evaluar expresiones. Esta guía cubre decoradores, validadores, schemas, inyección de dependencias, documentación, forward references, genéricos, errores, caché, imports circulares y seguridad.

__annotations__ básico

def sumar(a: int, b: int) -> int:
    return a + b

print(sumar.__annotations__)

El diccionario contiene las anotaciones declaradas, pero algunos valores pueden ser strings u objetos no resueltos según la versión y la configuración del módulo.

Usar get_type_hints

from typing import get_type_hints

hints = get_type_hints(sumar)
print(hints)

El resultado mapea nombres de parámetros y la clave especial return a objetos de tipo resueltos cuando es posible.

Referencias futuras

class Usuario:
    gerente: "Usuario | None"

print(Usuario.__annotations__)
print(get_type_hints(Usuario))

El acceso directo puede mostrar una string. get_type_hints intenta resolver Usuario y construir la unión real.

Anotaciones diferidas

Cuando el módulo usa evaluación diferida, las expresiones permanecen sin evaluar. get_type_hints es una forma centralizada de resolverlas siempre que los nombres estén disponibles.

Namespaces globales y locales

get_type_hints(objeto, globalns=globales, localns=locales)

Los namespaces explícitos ayudan con clases dinámicas, funciones anidadas y herramientas que inspeccionan objetos fuera de su módulo original. Proporciona mappings correctos y mínimos.

Fallos de resolución

class Pedido:
    cliente: "Cliente"

Si Cliente no está disponible, get_type_hints puede lanzar NameError. Las herramientas deben capturar el fallo, identificar la anotación y decidir si aceptan valores no resueltos.

Annotated se elimina por defecto

from typing import Annotated

def edad(valor: Annotated[int, "0 a 130"]) -> None:
    ...

Sin opciones extra, el resultado puede contener solo int.

Conservar extras

hints = get_type_hints(edad, include_extras=True)

include_extras=True conserva Annotated, Required, NotRequired y calificadores relacionados. Consulta Annotated en Python.

Inspeccionar Annotated

from typing import get_args, get_origin

anotacion = hints["valor"]
print(get_origin(anotacion))
print(get_args(anotacion))

Usa las APIs de typing en lugar de analizar representaciones de texto. get_args() devuelve el tipo base y los metadatos.

Funciones decoradas

Los decoradores deben usar functools.wraps para conservar metadatos y __wrapped__. Un wrapper mal implementado puede ocultar las anotaciones.

from functools import wraps

def log(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        return func(*args, **kwargs)
    return wrapper

Clases y métodos

class Configuracion:
    host: str
    puerto: int

print(get_type_hints(Configuracion))

Para clases, la función combina anotaciones a lo largo del MRO. Las definiciones derivadas tienen prioridad.

Herencia

class Base:
    id: int

class Derivada(Base):
    nombre: str

get_type_hints(Derivada)

El resultado puede incluir id y nombre. Una anotación derivada sustituye a la base con el mismo nombre.

ClassVar y Final

Con extras preservados, un framework puede distinguir campos de instancia, variables de clase y declaraciones finales. Cada herramienta decide cómo documentarlos o ignorarlos.

TypedDict

get_type_hints recupera tipos de valores, mientras que la presencia de claves depende también de __required_keys__ y __optional_keys__. Un generador de schema debe combinar ambas fuentes.

Dataclasses

En dataclasses, get_type_hints resuelve tipos y dataclasses.fields() aporta defaults, factories, flags y metadatos. Los serializadores suelen necesitar las dos APIs.

Alias de tipo

Los alias modernos pueden mantenerse como entidades nombradas o evaluarse según la ruta de inspección. Decide cuándo expandir TypeAliasType y cuándo conservar su nombre. Consulta TypeAliasType en Python.

Genéricos

from typing import Generic, TypeVar

T = TypeVar("T")

class Caja(Generic[T]):
    valor: T

get_type_hints recupera T, pero no sustituye automáticamente int en todos los contextos de Caja[int]. La especialización concreta requiere orígenes, argumentos y bases genéricas.

Objetos no soportados

No todo objeto expone anotaciones útiles. Built-ins y extensiones C pueden carecer de metadatos completos. Valida la entrada y produce mensajes claros.

Evaluación y seguridad

Las anotaciones pueden contener expresiones. Resolverlas puede ejecutar código en ciertos escenarios. No trates anotaciones de fuentes no confiables como datos inertes. Limita namespaces y evita importar módulos desconocidos solo para resolver hints.

No es un validador directo

get_type_hints describe el contrato declarado, pero no prueba valores. Un validador debe interpretar uniones, colecciones, TypedDict, Protocol, Annotated, recursión y metadatos personalizados.

Caché

Resolver repetidamente puede ser costoso. Los frameworks suelen guardar resultados por función o clase. Modificar anotaciones dinámicamente complica la invalidación y es mejor evitarlo.

Imports circulares

Las referencias futuras reducen imports superiores, pero get_type_hints necesita nombres durante la resolución. Organiza módulos, usa namespaces explícitos y retrasa la evaluación hasta la inicialización cuando sea necesario.

Imports con TYPE_CHECKING

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from paquete import Cliente

El import existe solo para análisis estático. En runtime, get_type_hints puede no encontrar Cliente. Proporciona el nombre mediante un import real o globalns.

Validador de llamada simple

from inspect import signature
from typing import get_type_hints


def validar_llamada(func, *args, **kwargs):
    sig = signature(func)
    ligados = sig.bind(*args, **kwargs)
    hints = get_type_hints(func)
    for nombre, valor in ligados.arguments.items():
        esperado = hints.get(nombre)
        if isinstance(esperado, type) and not isinstance(valor, esperado):
            raise TypeError(f"{nombre} debe ser {esperado.__name__}")

La demostración soporta clases simples. Uniones, genéricos, Protocol y estructuras anidadas exigen un motor completo.

Generación de documentación

Una herramienta puede combinar inspect.signature(), docstrings, defaults y get_type_hints. Conserva alias públicos y Annotated cuando mejoren el contrato generado.

Errores comunes

  • Leer solo __annotations__: referencias pueden seguir siendo strings.
  • Olvidar include_extras: metadatos y calificadores desaparecen.
  • Ignorar NameError: algunos nombres no se pueden resolver.
  • Evaluar código no confiable: las anotaciones no siempre son datos seguros.
  • Tratar hints como validación: todavía hay que comprobar valores.
  • Resolver en cada llamada: puede ser necesario cachear.

Ejemplo completo: registro de handlers

from typing import get_type_hints

class Evento: ...
class Contexto: ...

handlers: dict[type[Evento], object] = {}

def handler(func):
    hints = get_type_hints(func, include_extras=True)
    evento = hints.get("evento")
    retorno = hints.get("return")
    if not isinstance(evento, type) or not issubclass(evento, Evento):
        raise TypeError("parámetro evento inválido")
    if retorno is not None and retorno is not type(None):
        raise TypeError("el handler debe devolver None")
    handlers[evento] = func
    return func

@handler
def procesar(evento: Evento, contexto: Contexto) -> None:
    ...

El decorador usa las anotaciones como API intencional. Una implementación real también comprobaría nombres, cantidad de parámetros y mensajes de error.

Cuándo evitar introspección

Si la interfaz puede registrarse explícitamente, pasar argumentos suele ser más simple y predecible. Usa get_type_hints cuando las anotaciones sean parte intencional de un framework, como serialización, inyección, validación o documentación.

Conclusión

get_type_hints() es la herramienta principal para recuperar anotaciones resueltas en runtime. Maneja referencias futuras, herencia y extras mejor que __annotations__.

La documentación oficial de get_type_hints en Python describe sus parámetros. Usa namespaces controlados, conserva extras cuando sea necesario, maneja fallos, cachea responsablemente y nunca evalúes anotaciones no confiables sin considerar el riesgo de ejecución.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    High-angle view of woman coding on a laptop, with a Python book nearby. Ideal for programming and tech content.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    assert_type y reveal_type: prueba inferencia de tipos

    Aprende assert_type y reveal_type en Python para inspeccionar inferencia, probar APIs tipadas y evitar regresiones estáticas.

    Ler mais

    Tempo de leitura: 4 minutos
    29/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    runtime_checkable en Python: Protocol runtime

    Aprende runtime_checkable en Python para comprobar Protocol con isinstance, entender límites y diseñar contratos estructurales seguros.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    Close-up of a laptop screen with code and a coffee mug, perfect for tech abstract themes.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Unpack en Python: kwargs y tipos variádicos

    Aprende typing.Unpack en Python para tipar **kwargs con TypedDict, expandir tuplas variádicas y conservar firmas precisas.

    Ler mais

    Tempo de leitura: 4 minutos
    29/08/2026
    Person holding Python logo sticker with blurred background, highlighting programming focus.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    Required y NotRequired: campos opcionales en TypedDict

    Aprende Required y NotRequired en Python para controlar claves obligatorias y opcionales de TypedDict sin confundir ausencia con None.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    Ball python slithering on a sunlit gravel pathway outdoors.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ReadOnly en Python: protege campos TypedDict

    Aprende ReadOnly en Python para proteger campos TypedDict, modelar contratos estables y evitar escrituras accidentales.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    Close-up view of a computer screen displaying code in a software development environment.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    TypeAliasType en Python: alias en runtime

    Aprende TypeAliasType en Python para crear alias explícitos, inspeccionarlos en runtime y modelar APIs genéricas reutilizables.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026