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

    Close-up view of a computer screen displaying code in a software development environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    get_origin e get_args: inspecione tipos genéricos

    Aprenda get_origin e get_args no Python para inspecionar genéricos, uniões, Annotated, Literal e aliases com segurança.

    Ler mais

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

    LiteralString no Python: strings confiáveis

    Aprenda LiteralString no Python para restringir SQL, templates e comandos a strings confiáveis e reduzir injeções com análise estática.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    dataclass_transform no Python: classes geradas

    Aprenda dataclass_transform no Python para tipar decorators, metaclasses e frameworks que geram __init__, campos e métodos.

    Ler mais

    Tempo de leitura: 6 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 Avançado
    Foto de perfil de Leandro Hirt da Academify

    TypeVarTuple no Python: genéricos variádicos

    Aprenda TypeVarTuple no Python para preservar tuplas heterogêneas, modelar dimensões e criar genéricos com vários parâmetros.

    Ler mais

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

    assert_type e reveal_type: teste inferência de tipos

    Aprenda assert_type e reveal_type no Python para inspecionar inferência, testar APIs tipadas e evitar regressões no analisador.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026
    Close-up image of a woman's hand holding a stack of spiral-bound notebooks and papers against a dark background.
    Python Avançado
    Foto de perfil de Leandro Hirt da Academify

    get_type_hints no Python: leia anotações

    Aprenda get_type_hints no Python para resolver referências futuras, ler Annotated e inspecionar funções e classes com segurança.

    Ler mais

    Tempo de leitura: 6 minutos
    29/08/2026