inspect.signature: inspecciona parámetros de funciones

Publicado el: 29/08/2026
Tempo de leitura: 5 minutos
A close-up shot showcasing the intricate scales of a snake, highlighting texture and color.

Funciones, métodos, clases y objetos invocables exponen un contrato: parámetros posicionales, argumentos por nombre, valores predeterminados, anotaciones y retorno. El módulo inspect permite leer ese contrato en runtime mediante inspect.signature(). Frameworks web, contenedores de dependencias, generadores de CLI, validadores, decorators, documentación y adapters dinámicos utilizan esta API.

Esta guía explica cómo obtener firmas, interpretar tipos de parámetros, vincular argumentos, aplicar defaults, conservar metadatos en decorators, crear firmas personalizadas y reconocer los límites de la introspección.

Primera firma

from inspect import signature

def crear_usuario(nombre: str, edad: int = 18) -> dict[str, object]:
    return {"nombre": nombre, "edad": edad}

sig = signature(crear_usuario)
print(sig)

El resultado no es solamente texto formateado. Un objeto Signature contiene parámetros ordenados, anotación de retorno y métodos que validan la forma de una llamada.

Recorrer parámetros

for nombre, parametro in sig.parameters.items():
    print(nombre)
    print(parametro.kind)
    print(parametro.default)
    print(parametro.annotation)

parameters es un mapping ordenado. El orden importa porque reproduce la declaración original y las reglas reales de invocación de Python.

Los cinco Parameter kinds

  • POSITIONAL_ONLY: solo acepta posición.
  • POSITIONAL_OR_KEYWORD: acepta posición o nombre.
  • VAR_POSITIONAL: representa *args.
  • KEYWORD_ONLY: aparece después de * y exige nombre.
  • VAR_KEYWORD: representa **kwargs.
def ejemplo(a, /, b, *args, c, **kwargs):
    ...

for p in signature(ejemplo).parameters.values():
    print(p.name, p.kind)

Un framework que genera formularios, rutas o comandos debe respetar estas categorías. Convertir todo en argumentos keyword rompe parámetros positional-only.

Defaults y anotaciones ausentes

Cuando no existe default o anotación, la API usa inspect.Parameter.empty.

from inspect import Parameter

for p in sig.parameters.values():
    if p.default is Parameter.empty:
        print(p.name, "es obligatorio")

No compares con None, porque None puede ser un valor predeterminado legítimo.

Anotación de retorno

from inspect import Signature

if sig.return_annotation is not Signature.empty:
    print(sig.return_annotation)

La anotación puede ser una clase, una expresión genérica, una string u otro objeto. signature() no sustituye typing.get_type_hints() cuando hay que resolver referencias futuras.

Validar llamadas con bind

vinculados = sig.bind("Ana", edad=30)
print(vinculados.arguments)

bind() aplica las mismas reglas de asignación que una llamada real y genera TypeError ante argumentos ausentes, duplicados o inesperados. Es útil en adapters, wrappers y sistemas de despacho.

Binding parcial

parcial = sig.bind_partial(nombre="Ana")

bind_partial() permite que falten parámetros obligatorios. Es apropiado para functools.partial, builders y configuraciones construidas por etapas. No lo uses cuando la llamada final ya debe estar completa.

Aplicar defaults

vinculados = sig.bind("Ana")
vinculados.apply_defaults()
print(vinculados.arguments)

Antes de apply_defaults(), el mapping contiene solo valores enviados. Después, los parámetros opcionales reciben sus defaults, *args se convierte en tupla vacía y **kwargs en diccionario vacío.

BoundArguments

El objeto vinculado expone args, kwargs y arguments. Una llamada normalizada puede reenviarse directamente:

resultado = crear_usuario(*vinculados.args, **vinculados.kwargs)

Modificar arguments cambia las propiedades derivadas. Hazlo con cuidado y valida tipos por separado, porque bind comprueba la estructura, no el significado.

Métodos y self

class Servicio:
    def ejecutar(self, tarea: str) -> None:
        ...

print(signature(Servicio.ejecutar))
print(signature(Servicio().ejecutar))

La firma del método no vinculado incluye self; la del método vinculado normalmente no. Un framework debe decidir si inspecciona el atributo de clase o el callable obtenido de una instancia.

Instancias invocables

class Conversor:
    def __call__(self, valor: str, *, estricto: bool = False) -> int:
        return int(valor)

print(signature(Conversor()))

signature() puede inspeccionar objetos con __call__. Esto es útil en pipelines, dependencias configurables y objetos estrategia.

Clases y constructores

class Usuario:
    def __init__(self, nombre: str, activo: bool = True):
        ...

print(signature(Usuario))

En clases, el contrato visible puede provenir de __call__ de la metaclase, __new__ o __init__. Una metaclase personalizada puede cambiar el resultado.

Decorators que ocultan firmas

def registrar(funcion):
    def wrapper(*args, **kwargs):
        print("llamada")
        return funcion(*args, **kwargs)
    return wrapper

Sin metadatos adicionales, la firma visible se convierte en (*args, **kwargs). Usa functools.wraps para definir __wrapped__:

from functools import wraps

def registrar(funcion):
    @wraps(funcion)
    def wrapper(*args, **kwargs):
        return funcion(*args, **kwargs)
    return wrapper

Por defecto, signature() sigue la cadena __wrapped__. Consulta la guía de decorators en Python.

follow_wrapped

signature(funcion_decorada, follow_wrapped=False)

Usa follow_wrapped=False cuando necesites inspeccionar la interfaz real del wrapper en lugar de la función original.

Firmas personalizadas

Los objetos pueden exponer __signature__. Los frameworks lo utilizan para presentar una interfaz pública generada dinámicamente. Esto modifica la introspección, no necesariamente el comportamiento. Una firma falsa puede indicar a las herramientas que una llamada es válida cuando el wrapper la rechaza.

Crear Parameter

from inspect import Parameter, Signature

parametro = Parameter(
    "limite",
    kind=Parameter.KEYWORD_ONLY,
    default=100,
    annotation=int,
)
nueva_firma = Signature([parametro], return_annotation=list)

Signature y Parameter son inmutables. Construye nuevos objetos o usa replace().

Modificar con replace

actualizada = sig.replace(return_annotation=dict[str, object])

Parameter.replace() puede cambiar nombre, default, kind o anotación. La secuencia resultante debe respetar el orden válido de Python: grupos posicionales primero y parámetros obligatorios antes de opcionales dentro de su categoría.

Anotaciones como strings

Con anotaciones diferidas, la firma puede contener strings. Versiones modernas ofrecen opciones de evaluación y formato, pero el código portable debe separar introspección estructural de resolución de tipos.

from typing import get_type_hints

hints = get_type_hints(crear_usuario, include_extras=True)

La guía de get_type_hints en Python explica namespaces, referencias futuras y seguridad.

Built-ins y extensiones

Muchas funciones implementadas en C exponen metadatos de firma, pero no todos los callables son introspectables. signature() puede generar ValueError cuando no existe firma y TypeError cuando el objeto no es invocable o no está soportado.

try:
    sig = signature(objeto)
except (TypeError, ValueError):
    sig = None

Inyección de dependencias

Un contenedor puede inspeccionar parámetros, resolver cada dependencia anotada y llamar a la función. Sin embargo, las anotaciones no validan en runtime. El contenedor necesita políticas para defaults, alias, parámetros variádicos, scopes y mensajes de error.

Generar una CLI

Parámetros obligatorios pueden convertirse en argumentos posicionales, keyword-only en opciones y bool en flags. La firma no incluye ayuda amigable, restricciones ni ejemplos. Metadatos de Annotated pueden complementar esa información.

Cache

La introspección repetida en rutas calientes puede tener coste. Como las firmas suelen ser estables, los frameworks las guardan por identidad del objeto. Invalida el cache si decorators o plugins modifican __signature__ dinámicamente.

Seguridad y privacidad

La inspección estructural no ejecuta el callable, pero resolver anotaciones puede evaluar nombres. No resuelvas hints de plugins no confiables sin aislamiento. Tampoco publiques defaults en documentación o logs si pueden contener tokens, rutas o credenciales.

Errores comunes

  • Comparar default con None: usa Parameter.empty.
  • Ignorar parameter kind: positional-only y keyword-only tienen reglas reales.
  • Confundir bind con validación de tipos: solo comprueba la forma.
  • Perder metadatos en decorators: aplica functools.wraps.
  • Suponer soporte en todo built-in: captura TypeError y ValueError.
  • Publicar un __signature__ engañoso: runtime e introspección pueden divergir.

Ejemplo completo: ejecutor configurable

from inspect import Parameter, signature
from typing import get_type_hints

def ejecutar(funcion, valores: dict[str, object]):
    sig = signature(funcion)
    hints = get_type_hints(funcion, include_extras=True)
    kwargs = {}

    for nombre, parametro in sig.parameters.items():
        if parametro.kind in {
            Parameter.VAR_POSITIONAL,
            Parameter.VAR_KEYWORD,
        }:
            continue

        if nombre in valores:
            kwargs[nombre] = valores[nombre]
        elif parametro.default is Parameter.empty:
            raise ValueError(f"falta valor: {nombre}")

    vinculados = sig.bind(**kwargs)
    vinculados.apply_defaults()
    return funcion(*vinculados.args, **vinculados.kwargs)

El ejemplo normaliza la llamada y detecta ausencias. Un ejecutor real debe validar tipos, manejar positional-only, convertir entradas y producir diagnósticos del dominio.

Conclusión

inspect.signature() convierte el contrato de un callable en objetos estructurados y fiables. Con Signature, Parameter y BoundArguments puedes validar la forma de llamadas, generar interfaces y preservar decorators sin analizar código fuente manualmente.

La documentación oficial de inspect.signature define la API completa. Combínala con get_type_hints(), respeta cada parameter kind y separa introspección, validación y ejecución.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    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

    total_ordering: genera comparaciones consistentes

    Aprende total_ordering en Python para generar comparaciones coherentes, devolver NotImplemented, integrar dataclasses y probar órdenes.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    Detailed shot of a Jungle Carpet Python (Morelia spilota cheynei) in its natural habitat.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    get_origin y get_args: inspecciona tipos genéricos

    Aprende get_origin y get_args en Python para inspeccionar genéricos, uniones, Annotated, Literal, alias y metadatos de runtime.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    Striking image of a red-bellied python showcasing its vibrant scales in dramatic lighting.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    LiteralString en Python: cadenas confiables

    Aprende LiteralString en Python para restringir SQL, templates y comandos a cadenas confiables y reducir riesgos de inyección.

    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

    dataclass_transform en Python: clases generadas

    Aprende dataclass_transform en Python para tipar decorators, clases base y metaclases que generan campos, __init__ y métodos.

    Ler mais

    Tempo de leitura: 5 minutos
    29/08/2026
    A person typing on a laptop with a Python programming book visible, capturing technology and learning.
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    TypeVarTuple en Python: genéricos variádicos

    Aprende TypeVarTuple en Python para conservar tuplas heterogéneas, modelar dimensiones y crear genéricos con parámetros variables.

    Ler mais

    Tempo de leitura: 4 minutos
    29/08/2026
    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