El módulo inspect permite examinar funciones, métodos, clases y otros objetos durante la ejecución. Uno de sus recursos más útiles es inspect.signature(), que devuelve una representación estructurada de los parámetros de una función. El objeto Signature incluye el método bind(), responsable de asociar argumentos posicionales y nombrados con los parámetros correctos, respetando las reglas normales de llamada de Python.
Qué representa una firma
La firma describe parámetros obligatorios, valores predeterminados, parámetros solo posicionales, parámetros solo por nombre, *args y **kwargs. Esta información es útil en decoradores, frameworks, sistemas de inyección de dependencias, herramientas de línea de comandos, pruebas automatizadas, plugins y documentación generada.
from inspect import signature
def calcular_total(precio, cantidad=1, *, descuento=0):
return precio * cantidad * (1 - descuento)
firma = signature(calcular_total)
print(firma)El resultado no es solo texto. Puede inspeccionarse, reutilizarse y validarse. Para ampliar conceptos relacionados, consulta nuestros artículos sobre funciones en Python, args y kwargs, decoradores y type hints.
Cómo funciona bind
Se llama a bind() con los mismos argumentos que recibiría la función. Si la llamada es válida, Python devuelve un objeto BoundArguments. Su atributo arguments contiene un mapeo entre nombres de parámetros y valores.
vinculados = firma.bind(100, 2, descuento=0.1)
print(vinculados.arguments)Si falta un argumento obligatorio, aparece una palabra clave inesperada o un parámetro recibe dos valores, bind() genera TypeError. Esto evita recrear manualmente reglas complejas de resolución de argumentos.
bind frente a bind_partial
bind() exige una llamada completa. bind_partial() acepta información incompleta. Resulta útil cuando los datos se obtienen por etapas, al construir configuraciones o al implementar un comportamiento parecido a functools.partial.
parcial = firma.bind_partial(precio=150)
print(parcial.arguments)Una asociación parcial correcta no garantiza que la función pueda ejecutarse. Debe utilizarse solo cuando la ausencia de parámetros obligatorios sea intencional.
Aplicar valores predeterminados
El mapeo inicial contiene únicamente los valores enviados explícitamente. El método apply_defaults() agrega los valores predeterminados de los parámetros opcionales omitidos.
vinculados = firma.bind(100)
vinculados.apply_defaults()
print(vinculados.arguments)Esto es práctico para registros estructurados, auditorías, validación y normalización de configuraciones. Los parámetros variádicos reciben valores vacíos adecuados: una tupla vacía para *args y un diccionario vacío para **kwargs.
Ejecutar la función después de validar
BoundArguments ofrece las propiedades args y kwargs. Estas reconstruyen la llamada en el orden correcto.
vinculados = firma.bind(100, descuento=0.2)
vinculados.apply_defaults()
resultado = calcular_total(*vinculados.args, **vinculados.kwargs)Este patrón es especialmente útil en decoradores genéricos, porque el envoltorio no necesita conocer de antemano los nombres de todos los parámetros.
Decorador de validación
from functools import wraps
from inspect import signature
def exigir_no_negativos(func):
firma = signature(func)
@wraps(func)
def wrapper(*args, **kwargs):
vinculados = firma.bind(*args, **kwargs)
vinculados.apply_defaults()
for nombre, valor in vinculados.arguments.items():
if isinstance(valor, (int, float)) and valor < 0:
raise ValueError(f'{nombre} no puede ser negativo')
return func(*vinculados.args, **vinculados.kwargs)
return wrapperEl decorador puede trabajar con distintas funciones porque Python se encarga de la asociación. En producción conviene considerar valores opcionales, booleanos, tipos esperados y reglas específicas del negocio.
Parámetros especiales
Signature.bind() respeta parámetros solo posicionales antes de /, parámetros solo por nombre después de *, argumentos posicionales variables y palabras clave adicionales. Esta fidelidad lo hace más confiable que una solución basada únicamente en contar argumentos.
Siempre que sea posible, conserva los mensajes de error originales de Python. También conviene distinguir un TypeError producido durante la asociación de otro generado dentro del cuerpo de la función. Una captura demasiado amplia puede ocultar errores reales.
Casos de uso
Un framework web puede mapear variables de ruta y datos de una petición hacia parámetros de una función. Un ejecutor de tareas puede convertir JSON en llamadas. Un framework de pruebas puede inyectar fixtures por nombre. Una biblioteca de línea de comandos puede crear opciones automáticamente. Un sistema de plugins puede comprobar si una extensión cumple un contrato.
Los registros estructurados son otro caso importante. En vez de guardar una tupla sin contexto, el sistema puede registrar nombres significativos. Sin embargo, contraseñas, tokens, claves y datos personales deben ocultarse antes de escribirlos en un log.
Rendimiento y seguridad
Inspeccionar firmas tiene un coste. En rutas muy utilizadas, crea el objeto Signature una sola vez y reutilízalo. Los decoradores deberían construirlo al aplicarse, no en cada llamada.
El binding valida la forma de una llamada, pero no convierte la ejecución en segura. Nunca permitas que una entrada no confiable seleccione cualquier función. Usa un registro controlado de funciones permitidas, valida valores y aplica reglas de autorización.
Algunas funciones de extensiones y objetos especiales no exponen metadatos completos. signature() puede generar ValueError o TypeError. Las herramientas que aceptan objetos de diferentes orígenes deben tratar estos casos explícitamente.
Pruebas recomendadas
Prueba llamadas posicionales, llamadas por nombre, valores predeterminados, parámetros solo posicionales, parámetros solo por nombre, *args, **kwargs, valores duplicados, argumentos ausentes y palabras clave inesperadas. Comprueba también que los cambios realizados en bound.arguments se reflejen correctamente en args y kwargs.
Buenas prácticas
Crea y reutiliza la firma. Utiliza bind() para llamadas completas y bind_partial() solo cuando la información incompleta sea intencional. Ejecuta apply_defaults() cuando necesites una vista normalizada. Usa bound.args y bound.kwargs para conservar la semántica de llamada. Oculta secretos en los logs y limita la ejecución dinámica a funciones aprobadas.
La documentación oficial de inspect en Python explica Signature, Parameter y BoundArguments. La referencia del lenguaje Python detalla las definiciones de funciones y las reglas de parámetros.
Conclusión
inspect.signature().bind() transforma una llamada sin procesar en una estructura clara, validada y editable. Evita asociaciones manuales frágiles y proporciona a decoradores, frameworks, plugins, pruebas y sistemas de automatización una forma confiable de comprender llamadas. Junto con apply_defaults(), args y kwargs, es una de las herramientas más prácticas de introspección en Python.







