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

    Estructura de código y XML para canonicalización con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ElementTree canonicalize: normaliza XML en Python

    Aprende ElementTree canonicalize en Python para comparar XML, generar hashes estables y controlar namespaces, espacios y comentarios.

    Ler mais

    Tempo de leitura: 5 minutos
    22/10/2026
    Código Python en una pantalla
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    T-strings y templatelib: interpolación estructurada

    Aprende t-strings y string.templatelib en Python para interpolación estructurada, renderizado seguro, logs y procesamiento personalizado.

    Ler mais

    Tempo de leitura: 6 minutos
    22/10/2026
    Base SQLite y código Python para exportación selectiva con iterdump
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    SQLite iterdump: crea dumps selectivos en Python

    Aprende sqlite3.Connection.iterdump con filtro para crear dumps SQLite selectivos, validar dependencias y restaurar con seguridad.

    Ler mais

    Tempo de leitura: 4 minutos
    21/10/2026
    Código y archivos que representan compresión de datos con Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    compression.zstd: Zstandard rápido en Python

    Aprende compression.zstd en Python para comprimir archivos y datos con velocidad, streaming, integridad y seguridad.

    Ler mais

    Tempo de leitura: 7 minutos
    21/10/2026
    Archivo de configuración y estructura de código para ConfigParser en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    ConfigParser: lee INI sin sección en Python

    Aprende ConfigParser sin sección en Python con validación, tipos, compatibilidad, capas de configuración y escritura segura.

    Ler mais

    Tempo de leitura: 5 minutos
    20/10/2026
    Datos binarios y memoria de ordenador representando memoryview en Python
    Python Avanzado
    Foto de perfil de Leandro Hirt da Academify

    memoryview count e index: busca sin copiar

    Aprende memoryview count e index en Python para buscar valores en buffers sin copias y validar datos binarios con eficiencia.

    Ler mais

    Tempo de leitura: 5 minutos
    20/10/2026