inspect.signature: leia parâmetros de funções

Publicado em: 29/08/2026
Tempo de leitura: 5 minutos
Close-up view of a computer screen displaying code in a software development environment.

Funções, métodos e objetos chamáveis carregam um contrato: parâmetros posicionais, argumentos nomeados, valores padrão, anotações e retorno. O módulo inspect permite ler esse contrato em runtime por meio de inspect.signature(). A API é usada por frameworks web, sistemas de injeção de dependência, CLIs, validadores, decorators, ferramentas de documentação e bibliotecas que adaptam chamadas dinamicamente.

Neste guia, você aprenderá a obter assinaturas, interpretar tipos de parâmetros, vincular argumentos com bind(), aplicar defaults, preservar metadados em decorators, modificar assinaturas e reconhecer os limites da introspecção.

Primeira assinatura

from inspect import signature

def criar_usuario(nome: str, idade: int = 18) -> dict[str, object]:
    return {"nome": nome, "idade": idade}

sig = signature(criar_usuario)
print(sig)

O resultado representa a assinatura completa. Ele não é apenas texto: Signature possui objetos Parameter, anotação de retorno e métodos para validar chamadas.

Percorrendo parâmetros

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

parameters é um mapeamento ordenado. A ordem importa porque reproduz a declaração original e as regras de chamada do Python.

Tipos de Parameter

Cada parâmetro possui um kind. Os cinco tipos são:

  • POSITIONAL_ONLY: só pode ser enviado por posição.
  • POSITIONAL_OR_KEYWORD: aceita posição ou nome.
  • VAR_POSITIONAL: representa *args.
  • KEYWORD_ONLY: aparece depois de * e exige nome.
  • VAR_KEYWORD: representa **kwargs.
def exemplo(a, /, b, *args, c, **kwargs):
    ...

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

Frameworks que geram formulários ou comandos precisam respeitar essas categorias. Transformar tudo em argumento nomeado pode quebrar parâmetros position-only.

Valores ausentes

Quando um parâmetro não possui default ou anotação, a API usa inspect.Parameter.empty.

from inspect import Parameter

for p in sig.parameters.values():
    if p.default is Parameter.empty:
        print(p.name, "é obrigatório")

Não compare com None, pois None pode ser um default legítimo.

Anotação de retorno

from inspect import Signature

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

A anotação pode ser uma classe, expressão genérica, string ou outro objeto. signature() não substitui typing.get_type_hints() quando referências futuras precisam ser resolvidas.

Validando chamadas com bind

argumentos = sig.bind("Ana", idade=30)
print(argumentos.arguments)

bind() aplica as mesmas regras de chamada da função e gera TypeError em caso de argumento ausente, duplicado ou inesperado. Isso é muito útil para adapters e sistemas de despacho.

Validação parcial

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

bind_partial() permite omitir argumentos obrigatórios. Ele é apropriado para functools.partial, builders e configurações montadas em etapas. Não o use quando a chamada final precisa estar completa.

Aplicando defaults

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

Antes de apply_defaults(), apenas argumentos enviados aparecem no mapeamento. Depois, parâmetros opcionais recebem seus padrões, *args vira tupla vazia e **kwargs vira dicionário vazio.

BoundArguments

O objeto retornado por bind mantém args, kwargs e arguments. Isso permite normalizar uma chamada e repassá-la:

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

Ao alterar arguments, as propriedades derivadas refletem a mudança. Faça isso com cautela e valide tipos separadamente, pois bind verifica formato, não semântica.

Métodos e self

class Servico:
    def executar(self, tarefa: str) -> None:
        ...

print(signature(Servico.executar))
print(signature(Servico().executar))

A assinatura do método não vinculado inclui self; a do método vinculado normalmente não. Frameworks precisam decidir se inspecionam a classe ou a instância.

Classes chamáveis

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

print(signature(Conversor()))

signature() pode inspecionar objetos que implementam __call__. Isso é útil em pipelines e dependências configuráveis.

Classes e construtores

class Usuario:
    def __init__(self, nome: str, ativo: bool = True):
        ...

print(signature(Usuario))

Para classes, a API representa a forma de construção conforme __call__, __new__ e __init__. Metaclasses personalizadas podem alterar o resultado.

Decorators podem esconder a assinatura

def registrar(funcao):
    def wrapper(*args, **kwargs):
        print("chamada")
        return funcao(*args, **kwargs)
    return wrapper

Sem cuidados, a assinatura visível vira (*args, **kwargs). Use functools.wraps para definir __wrapped__ e preservar metadados:

from functools import wraps

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

Por padrão, signature() segue a cadeia __wrapped__. O guia de decorators no Python explica esse padrão.

follow_wrapped

signature(funcao_decorada, follow_wrapped=False)

Defina follow_wrapped=False quando quiser inspecionar o wrapper real em vez da função original.

Assinaturas personalizadas

Objetos podem expor __signature__. Frameworks usam esse recurso para apresentar uma interface gerada dinamicamente. Contudo, o comportamento é uma convenção de introspecção; não muda automaticamente a aceitação real de argumentos.

Criando Parameter

from inspect import Parameter, Signature

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

Objetos são imutáveis. Use construtores ou replace() para criar novas versões.

Modificando com replace

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

Parameter.replace() altera nome, tipo, default ou anotação sem mutar o original. Respeite a ordem válida: parâmetros obrigatórios antes de opcionais e categorias na sequência aceita pelo Python.

Anotações em string

Com anotações adiadas, a assinatura pode conter strings. Em versões modernas, signature() possui opções relacionadas à avaliação e formatação, mas código portável deve separar introspecção estrutural da resolução de tipos.

from typing import get_type_hints

hints = get_type_hints(criar_usuario, include_extras=True)

Consulte get_type_hints no Python para referências futuras e namespaces.

Built-ins e extensões

Muitas funções implementadas em C oferecem metadados de assinatura, mas nem todas são introspectáveis. signature() pode lançar ValueError quando não existe assinatura e TypeError quando o objeto não é chamável.

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

Injeção de dependência

Um container pode percorrer parâmetros, resolver cada dependência pela anotação e chamar a função com valores montados. Entretanto, anotações não são validação. O container precisa tratar defaults, parâmetros variádicos, aliases e mensagens de erro.

Gerando uma CLI

Parâmetros obrigatórios podem virar argumentos posicionais; keyword-only podem virar opções; bool pode virar flag. Ainda assim, a assinatura não contém descrições amigáveis, regras de validação ou exemplos. Metadados extras podem vir de Annotated.

Cache

Inspecionar repetidamente funções em rotas quentes pode ter custo. Como funções e suas assinaturas normalmente são estáveis, frameworks podem manter cache por objeto. Invalide-o se decorators ou plugins modificarem __signature__ dinamicamente.

Segurança

A inspeção estrutural não executa a função, mas resolver anotações pode avaliar nomes. Não carregue módulos ou hints de plugins não confiáveis sem isolamento. Também evite expor defaults que contenham segredos em documentação ou logs.

Erros comuns

  • Comparar default com None: use Parameter.empty.
  • Ignorar parameter kind: position-only e keyword-only possuem regras reais.
  • Confundir bind com validação de tipos: ele verifica apenas a forma da chamada.
  • Perder assinatura em decorators: aplique functools.wraps.
  • Assumir suporte em todo built-in: trate TypeError e ValueError.
  • Modificar __signature__ sem adaptar runtime: introspecção e comportamento podem divergir.

Exemplo completo: executor configurável

from inspect import Parameter, signature
from typing import get_type_hints

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

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

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

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

O exemplo normaliza a chamada e detecta ausências, mas um executor real ainda precisa validar tipos, tratar position-only e construir mensagens específicas.

Conclusão

inspect.signature() transforma o contrato de um callable em objetos estruturados e confiáveis. Com Signature, Parameter e BoundArguments, você pode validar a forma de chamadas, gerar interfaces e preservar decorators sem analisar código-fonte manualmente.

A documentação oficial de inspect.signature detalha a API. Use-a com get_type_hints(), respeite parameter kinds e mantenha introspecção separada da validação de tipos e da execução.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Conteúdo do artigo

    Artigos relacionados

    Estrutura de código e XML para canonicalização com Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ElementTree canonicalize: normalize XML no Python

    Aprenda ElementTree canonicalize no Python para comparar XML, gerar hashes estáveis e controlar namespaces, espaços e comentários.

    Ler mais

    Tempo de leitura: 5 minutos
    22/10/2026
    Código de programação Python em uma tela
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    T-strings e templatelib: interpolação estruturada

    Aprenda t-strings e string.templatelib no Python para processar interpolações, criar renderizadores seguros e preservar dados estruturados.

    Ler mais

    Tempo de leitura: 6 minutos
    22/10/2026
    Banco SQLite e código Python para exportação seletiva com iterdump
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    SQLite iterdump: crie dumps seletivos no Python

    Aprenda sqlite3.Connection.iterdump com filtro para gerar dumps seletivos, validar dependências e restaurar bancos com segurança.

    Ler mais

    Tempo de leitura: 5 minutos
    21/10/2026
    Código e arquivos representando compressão de dados no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    compression.zstd: Zstandard rápido no Python

    Aprenda a usar compression.zstd no Python para comprimir e descomprimir dados com desempenho, streaming e segurança.

    Ler mais

    Tempo de leitura: 7 minutos
    21/10/2026
    Arquivo de configuração e estrutura de código para ConfigParser no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    ConfigParser: leia INI sem seção no Python

    Aprenda a ler arquivos INI sem cabeçalho usando ConfigParser, com validação, tipos, compatibilidade e escrita segura.

    Ler mais

    Tempo de leitura: 5 minutos
    20/10/2026
    Dados binários e memória de computador representando memoryview no Python
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    memoryview count e index: busque sem copiar

    Aprenda memoryview count e index no Python para buscar valores em buffers sem cópias e validar dados binários com eficiência.

    Ler mais

    Tempo de leitura: 5 minutos
    20/10/2026